Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.11.07;
Скачать: [xml.tar.bz2];

Вниз

Pchar   Найти похожие ветки 

 
GanibalLector ©   (2004-10-19 20:05) [0]

Итак,если сделать так:

var Buff:Pchar;
begin
...
Buff:="12345798...7989";//ОЧЕНЬ ДЛИННАЯ СТРОКА!!!
end;
то,получаю String literals may have at most 255 element

но,если так:
Buff:="12345687"+
"7346..234234"+"23423424..234234";
то все в порядке.

Что это за дела ???

P.S. И еще,какая граница у Pchar??? ИМХО,что-то в районе 4 метров.


 
GanibalLector ©   (2004-10-19 20:09) [1]

И еще,вопрос...
Насколько обязательно использовать GetMem и FreeMem???И в каких случаях??? Дело в том,что без них,все отлично,а с ними получаю ошибку...


 
jack128 ©   (2004-10-19 20:17) [2]

GanibalLector ©   (19.10.04 20:05)
Что это за дела ???

особенности IDE


> P.S. И еще,какая граница у Pchar??? ИМХО,что-то в
> районе 4 метров.

Что значит "граница"?? Максимальный размер?? 2 Гига..

GanibalLector ©   (19.10.04 20:09) [1]
Насколько обязательно использовать GetMem и FreeMem??? Дело в том,что без них,все отлично,а с ними получаю ошибку...


ну например вот в таком коде абсолютно не обязательно..

var
 i: Integer;
begin
 i := 0;
end;

ps вроде опытный форумец, а вопросы задовать не умеешь ;-)


 
GanibalLector ©   (2004-10-19 20:27) [3]

>у например вот в таком коде абсолютно не обязательно..
Хорошо,а в таком???

var h:HWND;
buff:Pchar;ii:Cardinal;
begin
h:=CreateFile(pchar("C:\1.txt"), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if h=INVALID_HANDLE_VALUE then begin
messagedlg("Error"+(SysErrorMessage(GetLastError)),mterror,[mbok],0);
                               end else begin
// GetMem(buff,300); <-а тут предупреждение
buff:="testtstssssssssssssssss");
WriteFile(h,buff^,length(buff),ii,nil);
//  FreeMem(buff); <-ошибка
CloseHandle(h);
                               end;
end;


 
jack128 ©   (2004-10-19 20:38) [4]

GanibalLector ©   (19.10.04 20:27) [3]
// GetMem(buff,300); <-а тут предупреждение
buff:="testtstssssssssssssssss");


а какой?? У мя пятёрка не такая умная, чтобы мемлик увидеть.. А у тя он есть. Ты ж на следующей строке затираешь указатель..


 
GanibalLector ©   (2004-10-19 20:44) [5]

>а какой?? У мя пятёрка не такая умная, чтобы мемлик увидеть..
У меня тоже пятерка.Hint=value assigned to "buff" never used

>Ты ж на следующей строке затираешь указатель..
А...я понял,причину. Хорошо,так а что делать в таких случаях ???

З.Ы. jack128 ну ты понял...Ты лучший ;)


 
jack128 ©   (2004-10-19 21:07) [6]

GanibalLector ©   (19.10.04 20:44) [5]
Hint=value assigned to "buff" never used

а-а. понятно.. Просто System.GetMem - сompiler мagic функция, вот среда на неё так и реагирует.. Обычно, то что параметр var не говорит о том, что ему будет присвоено какое нить значение..


 
Piter ©   (2004-10-19 22:12) [7]

jack128 ©   (19.10.04 20:17) [2]
Что значит "граница"?? Максимальный размер?? 2 Гига..


ну это вряд ли система сможет выделить 2Гб "непрерывной" памяти. Если только в 64-битной ОС...

GanibalLector ©   (19.10.04 20:05)

вот как-то писал я тут типа статьи, правда, она нафиг никому не пригодилась, но может тебе поможет:
----------------------------------------------
В данной мини-статье предлагаю рассмотреть типы данных PChar и String.
Оба этих типа предназначены для хранения произвольного текста, только производят они это хранение по разному.
Замечу, что текст - это последовательный набор символов в памяти. Это верно для всех строковых типов.

PChar
Тип PChar  устроен очень просто. В памяти компьютера хранится массив символов, а сам PChar указывает на первый символ. Этот символ и последующие и будут составлять строку.
Но возникает вопрос - а где конец строки? На начало указыват сам PChar, а вот конец строки определяется по завершающему символу. Для PChar он равен #0  или, что тоже самое, байту $00.
Завершаюший символ имеет код #0, но он не имеет текстового представления. То есть, он не может встретиться в тексте, это не цифра, не буква и не знак препинания. Поэтому его и выбрали в качестве завершающего символа, чтобы встретив в тексте можно было сказать , что это конец строки, а не случайно попавшийся байт. И не надо путать завершающий код с кодом перевода строчки, имеющего в windows системах значение #13#10 (то есть, два байта, возвещающие о том, что дальше текст идет с новой строчки).
Получается, что PChar - это строка от символа на который как раз и указывает PChar до завершающего символа.

Перед тем как использовать PChar нужно выделить память под него. Причем, если вы хотите чтобы можно было записать 255 символов, то нужно выделить 256 байт памяти - ведь нужен еще один байт на завершащий символ.

Приведем пример вызова WinApi функции GetWindowsDirectory, которая позволяет узнать путь к директории Windows. Покажем только принципиальную часть, практически настоятельно рекомендуется использовать тип AnsiString (который будет рассмотрен далее) для работы со строками. В реальных задачах не имеет смысла работать с PChar.
Итак, пример:

var
 P:PChar;
begin
 GetMem(P,MAX_PATH+1); //выделяем память под PChar
 GetWindowsDirectory(P,MAX_PATH); // вызываем API функцию. Причем, даем понять, что
 // готовы записать в наш буфер только MAX_PATH символов, хотя выделили MAX_PATH+1 байт
...


Все достаточно просто. Только после использования переменной P не забудьте очистить память командой FreeMem(P) во избежание утечек памяти.

Есть некоторые нюансы, которые иногда вводят в заблуждение неопытных программистов. Рассмотрим такой фрагмент программы:

var
 P:PChar;
begin
 P:="Hello, world";


Строчка P:="Hello, world";  на самом деле не имеет смысла. Ведь PChar - это указатель на первый символ строки. Как любой указатель в 32-ух битных системах, он также является 32-ух битным и никак не может равняться "Hello, World". Но компилятор Delphi для упрощения нашей с вами жизни все понимает правильно.
Некоторые думают, что при исполнении данной команды выделяется память, достаточная для размещения строчки "Hello, world" и потом эта строчка копируется в выделенную память. Но все совсем не так! Зачем что-то куда-то копировать? Понятное дело, что строка "Hello, World" при компиляции  будет "зашита" где-то в exe"шнике. При исполнении этого exe файла строчка также будет спроецирована в адресное пространство процесса (в сегмент данных) и будет иметь вполне определенный и постоянный адрес на всем протяжении исполнения программы. Так можно просто присвоить P адрес этой строчки? Что компилятор и сделает. Delphi строчку P:="Hello, world"; выполнит таким образом: просто присвоит P адрес буквы "H" в адресном пространстве процесса. Этот адрес будет известен еще на стадии компиляции.
При этом никакого выделения памяти не будет! И если с P вы потом делать ничего не будете - то и память освобождать не надо!
Код:

var
 P:PChar;
begin
 P:="Hello, world";
 FreeMem(P);
end;


Некорректный. Вы пытаетесь освобождать память, которую никто не выделял.

Ну и напоследок. Если даны переменные P1 и P2 типа PChar, то их присвоение:
P1:=P2
приведет к копированию адреса на который указывает P2 в P1. То есть, обе переменных будут ссылаться на одну и ту же строчку в памяти (на одну и ту же область памяти).

PChar не лишен недостатков. Допустим, вам надо работать с произвольным набором байтов, а не с текстом. Тут PChar не подходит, потому что в таком наборе случайно может встретиться символ #0, который будет принят за окончания строки, хотя это и не так.

String
Сначала определимся, что String - это тип, зависящий от директив компилятора. При директиве {$H+}, которая установлена по умолчанию, String интерпретируется как AnsiString, если {$H-} - то как ShortString.

Подумаем - как побороть недостаток PChar"а? Выход прост - пусть строка имеет такой формат:
<количество_символов> <сама_строка>.
Именно такой формат имеет ShortString. Первый байт этой строки указывает количество последующих символов. Таким образом, можно хранить любые данные, компьютер не остановится, увидев завершающий символ, так как конец строки определяется по другому признаку. Если длина 15 - то он будет обрабатывать именно 15 символов (байт), независимо от того, что в них хранится. ShortString не имеет завершающего символа в конце, поэтому не совместим с WinApi, где нужно передавать строки именно с завершающим символом.

Недостаток типа ShortString заключается в его названии :) Он достаточно короткий. Так как на длину строки отводится 1 байт, то она не может принимать значение более 255, а значит ShortString не может хранить более 255 символов.

Плавно переходим к типу AnsiString (по умолчанию этому типу и равен String). Тут все достаточно сложно, данная строка хранит размер выделенной памяти, счетчик ссылок, число символов. Мы не будет рассматривать побайтово его структуру в памяти. В любом


 
Piter ©   (2004-10-19 22:12) [8]

случае, внутренняя реализация скрыта от программиста и использовать тип AnsiString очень удобно.
На размер строки отводится 4 байта, соответственно, AnsiString может хранить огромное количество символов (порядка 2 Гб), что покроет с лихвой любые нужды.
К тому же, AnsiString совместим с типами с завершающими символами в конце. Это означает, что его можно использовать для вызова функций WinApi.
Также стоит упомянуть, что ShortString статический тип, то есть под него однажды выделяют место в памяти и он так и остается там по одному адресу.
AnsiString же тип динамический, эта строка может менять свою длину и "перемещаться" по памяти в поисках наилучшего места для размещения. То есть, AnsiString может менять свой адрес в памяти при изменении строки.

Преобразования типов
Итак, как преобразовать тип PChar к AnsiString и наоборот?

AnsiString->PChar
Рассмотрим на примере вызовов функций WinApi, которые принимают параметры типа PChar. А мы работаем в Дельфи и хотим использовать String (точнее AnsiString).

Рассмотрим вызов функции:
function MessageBox(hWnd: HWND; lpText, lpCaption: PChar; uType: UINT): Integer; stdcall;
которая вызывает на экране появление бокса с сообщением.

Параметры lpText и lpCaption являются PChar.
Указываем компилятору явно преобразовать AnsiString в Pchar:

procedure TForm1.Button1Click(Sender: TObject);
var s1, s2:string;
begin
 s1:="text";
 s2:="caption";
 MessageBox(0,PChar(s1),PChar(s2),0);
end;


Вот и все!

Тут все ясно. Но что делать, когда нужно передать PChar для того, чтобы система заполнила его каким-нибудь значением?

Рассмотрим вызов уже знакомой функции
GetWindowsDirectory(lpBuffer: PChar; uSize: UINT): UINT;
которая записывает путь к директории Windows в передаваемую переменную lpBuffer.

procedure TForm1.Button1Click(Sender: TObject);
var
 s:string;
begin
 setlength(s,MAX_PATH);
 GetWindowsDirectory(PChar(s),MAX_PATH);
 ...


В общем, это аналог вызова GetWindowsDirectory с PChar"ом, который мы рассматривали ранее.
Все тоже самое, НЕ ЗАБЫВАЕМ ВЫДЕЛИТЬ ПАМЯТЬ для переменной S перед использованием:
setlength(s,MAX_PATH)
В отличии от GetMem для выделения памяти PChar, для AnsiString применяется SetLength (причем, не надо учитывать завершающий символ).

После вызова GetWindowsDirectory в строке S также будет находится MAX_PATH символов (сколько выделили - столько и есть, а выделили по максимуму - откуда знать сколько памяти занимает путь к директории с Windows). Причем, скорее всего большинство их них пробелы, которые вовсе не нужны. Поэтому, перед использованием такой строки отрежем ненужные символы. Так как GetWindowsDirectory возвращает количество записанных символов, то код очевиден:

procedure TForm1.Button1Click(Sender: TObject);
var
 s:string;
 Length: integer;
begin
 setlength(s,MAX_PATH);
 Length := GetWindowsDirectory(PChar(s),MAX_PATH);
 setlength(s,Length); // укорачиваем строчу, выделяя ей меньше памяти
 // при этом все символы после позиции Length будут отрезаны
 ...


Ну а теперь рассмотрим сложный вариант.


var p:PChar;
...
p:=PChar(s); // где-то есть переменная s и в ней хранится некая строка


При этом p начинает указывать на ту же область памяти, что и s. И все будет замечательно, пока с s ничего не происходит. Но вот например:

var p:PChar;
   s:string;
begin
 setlength(s,MAX_PATH);
 setlength(s,GetWindowsDirectory(PChar(s),MAX_PATH));

 p:=PChar(s);
 s:=s+"SomeText";
 ...


Итак, строчками
setlength(s,MAX_PATH);
setlength(s,GetWindowsDirectory(PChar(s),MAX_PATH));

в s заносится путь к директории windows. В памяти таким образом появляется некая строка, допустим "c:\windows", которой и равна s.
После чего идет
p:=PChar(s);
при этом p начинает указывать на ту же область памяти, где находится строка "c:\windows".
Потом s присваивается другое значение
s:=s+"SomeText";
А точнее к s должно быть прибавлено "SomeText". При этом менеджер памяти анализирует ситуацию, можно ли приписать данный текст прямо в ту же область памяти. Если память после "c:\windows" не занята, то он приписывает нужное количество символов. При этом S будет указывать по прежнему на тот же адрес в памяти, только увеличится счетчик символов. Соответстенно, и с P, указывающим туда же, будет все нормально. И после завершения кода P будет указывать на строчку "c:\windowsSomeText".
Это первый вариант развития событий.

Но если невозможно подряд разместить все новые символы, то менеджер памяти будет искать "дырку", свободную для размещения всех символов. При этом память, где хранится текущее "c:\windows" будет объявлена свободной, резервируется место для новой строки в другом месте, туда записывается значение "c:\windowsSomeText" и S начинает указывать на новый участок памяти, где хранится новое значение строки. А P по прежнему указывает на старый участок памяти! Скорее всего, там будет продолжать хранится строчка "c:\windows", потому как память никто не зачищал, но эта память является уже свободной. И в случае чего, данное место будет зарезервировано под иные нужды и там разместятся произвольные данные, в результате P будет указывать на черт знает что.
Для демонстрации можно выполнить такой пример:

var p:PChar;
   s:string;
   s2:string;
begin
 setlength(s,MAX_PATH);
 setlength(s,GetWindowsDirectory(PChar(s),MAX_PATH));

 p:=PChar(s);
 setlength(s2,1);
 s:=s+"SomeText";
end;


Тоже самое, но добавилась строчка setlength(s2,1);
Рассмотрим еще раз:
p:=PChar(s)
Что s, что P указывают на одну и ту же область памяти.
А потом
setlength(s2,1);
если ничего неожиданного не случится, то менеджер памяти Дельфи выделит под переменную s2 место сразу после s.
В результате, следующая строчка
s:=s+"SomeText";
будет обработана не так, как в предыдущем примере.


 
Piter ©   (2004-10-19 22:12) [9]

Места для добавления "SomeText" по старому адресу уже не хватит (ведь после s идет сразу s2), будет выбрано новое место и s перенесена туда, а "старая" память освобождена. Но P по прежнему ссылается на старое место! После выполнения этого кода, P скорее всего будет указывать на область памяти со значением "c:\windows" (но не факт, так как эта область памяти является свободной и может быть занята чем угодно), а не "c:\windowsSomeText"!
Поэтому будьте внимательны. Если S изменилось, то стоит перепресвоить PChar:
s:=s+"SomeText";
p:=PChar(s);


PChar->String

ну тут все просто. Если P указывает на некую строчку в памяти:

var s:string;
...
s:=p;


То произойдет неявное преобразование типов. При этом строка, на которую указывает p копируется в другую область памяти и s начинает указывать именно туда. В результате, просто появляются две переменные с одинаковыми значениями. И работа их друг от друга не зависит.


 
jack128 ©   (2004-10-19 22:27) [10]

Piter ©   (19.10.04 22:12) [7]
А ты на adm@delphimaster.ru Эту статью отослал?? Прикольно, будшь работу искать, в  резюме напишешь, что есть публикации ;-)

> , данная строка хранит размер выделенной памяти,
> счетчик ссылок, число символов.
про размер выделенной памяти.. Народ говорил, что в d6+ нету такого поля в StrRec. И в пятерке оно не используется..


 
VMcL ©   (2004-10-19 22:45) [11]

>>Piter ©  (19.10.04 22:12) [7]

>ну это вряд ли система сможет выделить 2Гб "непрерывной" памяти. Если только в 64-битной ОС...

А почему бы и нет? Например, если на компьютере будет всего 4 ГБ?


 
jack128 ©   (2004-10-19 22:50) [12]

VMcL ©   (19.10.04 22:45) [11]
почему бы эти 2 гига не выделить в файле подкачки??


 
Piter ©   (2004-10-19 23:20) [13]

jack128 ©   (19.10.04 22:27) [10]
А ты на adm@delphimaster.ru Эту статью отослал??


нет, да ведь это и не статья, недостойна она публикации такой. Путанно написано.

Я просто пытался кратко написать, так как писал ее для FAQ"а :)


 
Piter ©   (2004-10-19 23:22) [14]

VMcL ©   (19.10.04 22:45) [11]
А почему бы и нет? Например, если на компьютере будет всего 4 ГБ?


ты про оперативную память? А при чем здесь она?
ВАП процесса размером в 4 Гб.
Система себе оставляет 2 Гб - разработчику соответственно тоже 2 Гб. (хотя в win2000 и выше вроде есть опции, чтобы отхватить 3 Гб).
Так вот сомнительно, чтобы всю эту оставленную память в 2Гб можно было бы непрерывно захватить под строку :)


 
VMcL ©   (2004-10-19 23:40) [15]

>>Piter ©  (19.10.04 23:22) [14]

2 ГБ - 1 байт?
:-)

>хотя в win2000 и выше вроде есть опции, чтобы отхватить 3 Гб

Отож.


 
Piter ©   (2004-10-20 00:54) [16]

VMcL ©   (19.10.04 23:40) [15]
2 ГБ - 1 байт?


ну а проецирование exe"шника хотя бы? :)


 
GanibalLector ©   (2004-10-20 01:04) [17]

Спасибо всем за внимание...
2 Piter
вот как-то писал я тут типа статьи, правда, она нафиг никому не пригодилась, но может тебе поможет
Очень даже помогла.Надеюсь впредь проблем не возникнет.Спасибо еще раз.


 
jack128 ©   (2004-10-20 01:55) [18]

Piter ©   (20.10.04 0:54) [16]
ну а проецирование exe"шника хотя бы? :)


Вот что рассылка RSDN говорит

Каждому процессу (начиная с Windows 95) выделяется собственное виртуальное адресное пространство. Для 32-разрядных процессов его размер составляет 4 Гб. Это адресное пространство разбивается на разделы, функциональное назначение и свойства которых довольно сильно отличаются у семейств ОС WinNT и Win9Х.

Адресное пространство любого процесса в Win9Х можно разделить на три раздела:

Младшие два гигабайта (00400000-7FFFFFFF) - код и данные пользовательского режима (в диапазоне 00000000-003FFFFF расположены разделы для выявления нулевых указателей и для совместимости с программами DOS и Win16);
Третий гигабайт - для общих файлов, проецируемых в память (MMF), и системных DLL.
Четвёртый гигабайт - для кода и данных режима ядра (здесь располагается ядро операционной системы и драйверы).
Старшие два гигабайта являются общими для всех процессов. Основные системные DLL - kernel32.dll, advAPI32.dll, user32.dll и GDI32.dll загружаются в третий гигабайт. По этой причине эти четыре библиотеки доступны всем процессам в системе. Поскольку этот гигабайт общий, они существуют во всех процессах по одним и тем же адресам. Из соображений безопасности Microsoft запретила запись в область, куда они загружаются. Если же запись туда всё же произвести (а это возможно из режима ядра или недокументированными методами), то изменения произойдут во всех процессах одновременно.

В WinNT общих разделов у процессов нет, хотя системные библиотеки по-прежнему во всех процессах загружаются по одинаковым адресам (но теперь уже в область кода и данных пользовательского режима). Запись в эту область разрешена, но у образов системных библиотек в памяти стоит атрибут «копирование при записи» (copy-on-write). По этой причине попытка записи, например, в образ kernel32.dll приведёт к появлению у процесса своей копии изменённой страницы kernel32.dll, а на остальных процессах это никак не отразится.


Отсюда - 2 верхних гига, заняты системой и MMF, да младшие 4 МБ - зарезервированы.  Отсюда следует макс размер памяти, который может выделить приложение = 2 ГБ - 4МБ - <размер EXE> - <размер всех загруженных не системных DLL> , меньше 2ГБ. Поздравляю Piter, ты прав  :-)


 
GanibalLector ©   (2004-10-20 02:00) [19]

2 jack128
Я кстати вспомнил.Как-то читал книгу(ща займусь поиском названия,автора и стр.)так вот,автор утверждал что размер 4Г.


 
default ©   (2004-10-20 02:08) [20]

[19]
ты не понял скорее всего что имел ввиду автор
диапазон адресов 4 гига
а часть этого количества под пользов-ие данные и код порядка двух гигов


 
default ©   (2004-10-20 02:10) [21]

"Впервые увидев адресное пространство своего 32-разрядного процесса, я был удивлен тем, что его полезный объем чуть ли не вдвое меньше. Неужели раздел для кода и данных режима ядра должен занимать столько места? Оказывается — да .Это пространство нужно системе для кода ядра, драйверов устройств, кэш-буферов ввода-вывода, областей памяти, не сбрасываемых в файл подкачки, таблиц, используемых для контроля страниц памяти в процессе и т д По сути, Microsoft едва-едва втиснула ядро в эти виртуальные два гигабайта. "
Рихтер...


 
VMcL ©   (2004-10-20 07:45) [22]

>>jack128 ©  (20.10.04 01:55) [18]

В сабдже не Win95 и не WinNT, а WinXP. Не помню, есть ли в XP/2000 не серверных, но в серверных версиях Win есть ключ для boot.ini, который позволяет расширить пользовательское АП до 3 ГБ, см. 14. Правда там еще есть особенности, связанные с линковкой, вроде.


 
Piter ©   (2004-10-20 18:28) [23]

jack128 ©   (20.10.04 1:55) [18]
Поздравляю Piter, ты прав  :-)


Кричали женщины "Ура!" и в воздух чепчики бросали! :)))



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2004.11.07;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.036 c
14-1098004825
QuasiLamo
2004-10-17 13:20
2004.11.07
Вот, чиста маё мнение


9-1088621303
kas-t
2004-06-30 22:48
2004.11.07
GLScene: плавное падение fps


1-1098646798
Zloy_SHREK
2004-10-24 23:39
2004.11.07
Проблема с полосой прокрутки.


4-1096056488
RyDmi
2004-09-25 00:08
2004.11.07
Получение текста из TMemo


14-1098186072
Herzog
2004-10-19 15:41
2004.11.07
Как разбить на строки...





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский