Загадочный тип PCHAR
6 октября 2006 года
Здравствуйте, дельфисты! Сегодня вам поведую, что это за тип PCHAR. И как его корректно использовать. Этот тип упоминается во всех API функциях, которые принимают в качестве параметра какое-либо строковое значение.
Сначала я расскажу вам про тип string. Тип string является главным преимуществом языка Pascal над языком С. Именно из-за этого типа программы, написанные на Pascal, весят больше, чем программы, написанные на С. Все знают, что тип string является массивом, котором каждый элемент является типом CHAR (следовательно, юникодовский тип WideString - массив из WideChar). Только размер этого массива неизвестен заранее и при каждом присваивании его длина изменяется. Но так же можно и при объявлении ограничить размер строки. Но размер строки ограничивается только формально, потому что нельзя обратиться к элементу массива больше чем размер строки указанной в разделе var. Конечно, размер каждой строки должен быть не более 2ГБ, примерно 2 миллиарда символов (для widestring 1 миллиард символов, так как один символ задаётся 2 байтами). Так как строка это массив, следовательно, через квадратные скобки можно обращаться к каждому элементу массива.
Str:='programmersclub.ru'; Str[1]:='P'; Str[5]:='L';
После данных манипуляций переменная str будет равна 'ProgLammersclub.ru', замечу, что первый символ в строке имеет индекс 1. Так как тип string и тип array of char сходны следователь их можно присвоить друг к другу. Но, при присваивании переменной массива переменной строки будет ошибка, так как у массива мы жёстко задаём размер массива, а у строки мы не знаем длину даже при её ограничении.
Var STR_ARR: ARRAY[1..60] of CHAR; STR1:Srring; STR2:String[20]; Begin ..... STR_ARR:=str1;//ошибка Str1:=STR_ARR: Str2:=STR_ARR;//нет ошибки
Обращаться к 55 символу переменной str2 уже нельзя, но физически в памяти он существует. При получении указателя на строку возвращается адрес первого символа в строке, значит, такие выражения одинаковы:
P1:=@str; P2:=@str[1];
Теперь тип PCHAR. Фактически тип PCHAR это указатель на тип CHAR. Это понятно и потому как он назван. По "программеским" правилам при объявлении нового типа типизированных указателей берётся тип, на который указывает указатель и спереди ставится буква P. Вот его объявление:
Type PCHAR: ^ CHAR;// следовательно также и тип WideChar PWIDECHAR: ^ WIDECHAR;
Ну, если этот тип указывает на только один символ, то, как же функции понимают параметры, которые мы передаём им. Всё очень просто. Каждая строка, переданная в качестве параметра какой либо функции должна иметь в конце символ #0. Функция по указателю находит первый символ строки и идёт дальше пока не наткнётся на символ #0. Delphi автоматизировала преобразование строки когда в параметре мы указываем саму строку: MessageBox(0,'привет','привет',0) здесь автоматика, а почему не автоматика при указывании переменных я не знаю. Мы всегда пишем
MessageBox(0,pchar(str),pchar(str),0);
Всё нормально. Тот же результат при использовании указателей.
MessageBox(0,@str,@str,0);
Это потому что массивы обычно заполняются нулями.
Пример 1: Совсем другая история:
var STR_ARR:array[1..2] of char; STR:String[6]; begin str_ARR[1]:='h'; str_ARR[2]:=#0; STR:='22'+#0; MessageBoxA(Handle,@STR,@str_ARR,MB_OK);
В сообщении перед двойкой стоит какой то символ. Это потому что нумерация в Delphi может начинаться с любого индекса, в данном случае она начинается 1, а 0 символ не используется. Значит то, что я вам сказал в начале это неправильно:
P1:=@str; P2:=@str[1]; //эти указатели будут отличаться на 1;
А массив выводится нормально, потому что Delphi автоматически правит указатель на массив.
Пример 2: Не знаю почему, но вместо двоек выводится не пойми что. Мистика!!!
var STR_ARR:array[1..2] of char; STR:String; //любая длина begin str_ARR[1]:='h'; str_ARR[2]:=#0; STR:='22'+#0; MessageBoxA(Handle,@STR,@str_ARR,MB_OK);
Пример 3:
var STR_ARR:array[1..2] of char; STR:String[4]; begin str_ARR[1]:='h'; str_ARR[2]:=#0; STR:='22'; MessageBoxA(Handle,@STR,@str_ARR,MB_OK);
Отсюда понятно что изначально все переменные заполняются нулями. И переменная STR тому не исключение.
Пример 4: Интересная ситуация:
var STR_ARR:array[1..2] of char; STR:String[4]; begin str_ARR[1]:='h'; str_ARR[2]:='s'; STR:='22'+#0; MessageBoxA(Handle,@STR,@str_ARR,MB_OK);
Как видите в заголовке есть символы 'hs' потом квадратик потом двойки. Всему есть разумное объяснение. Строка STR_ARR не кончается нулём, следовательно, функция не нашла нуль только в конце строки STR_ARR и пошла дальше к переменной STR, поэтому строка STR_ARR получилась такой длинной. Квадратик в после строки 'hs' понятен из первого примера.
Вот такие пироги. Изначально казалось, что тип string проще и лучше, а получилось как всегда! Тип string принёс нам массу неприятностей. Поэтому C++ намного популярнее Delphi. Вот такими словами кончается моя очередная статья.
Оставить комментарий
Комментарии
>> PCHAR: ^ CHAR;// следовательно также и тип WideChar
>> PWIDECHAR: ^ WIDECHAR;
такой код вызовет ошибку - тип в разделе type в паскале задается знаком = а не двоеточием;
>> Пример 2: Не знаю почему, но вместо двоек выводится не >> пойми что. Мистика!!!
>> var
>> STR_ARR:array[1..2] of char;
>> STR:String; //любая длина
>> begin
>> str_ARR[1]:='h';
>> str_ARR[2]:=#0;
>> STR:='22'+#0;
>> MessageBoxA(Handle,@STR,@str_ARR,MB_OK);
MessageBoxA выводит строку переданную в сишном формате, а ссылка на STR дает указатель на нулевой байт string (это байт хранящий количество символов строки, а сами символы string'a хранятся массивом начиная с первого байта)
вот и выводится абракадабра вместо двоек,
для этой цели надо брать @STR[1],
a str_ARR должен выводиться нормально, т.к. это массив char, а не string, в нем символы хранятся с нулевого байта(несмотря на то что нумерация в описании типа с единицы), в паскале байты массива в памяти хранятся с 0-го байта структуры данных
Подмена понятий какая-то. В строки с нормальным управлением памятью лезут с, по сути, поитером.
Вы б еще в языках со сборкой мусора с неуправляемыми поинтерами влезли, и рассуждали о том, что, GC плохо ибо "Изначально казалось, что тип string проще и лучше, а получилось как всегда! Тип string принёс нам массу неприятностей".
Плюс ошибки абсолютно не понятные.
PCHAR: ^ CHAR;// следовательно также и тип WideChar
PWIDECHAR: ^ WIDECHAR;
оБшибся, оБшибся..... спасибо за резензию статьи
и привел несколько примеров для лучшего понимания!!!
Не корректно, к 55 символу мы можем обратиться. Вот только мы выйдем за границу. А следовательно физически полученный нами символ не будет принадлежать строке.
caption:=Str2[ i ];
А это вообще шедевр.
PCHAR: ^ CHAR;// следовательно также и тип WideChar
PWIDECHAR: ^ WIDECHAR;
Во-первых, тип PChar нигде не объявляется, а является внутренним, системным.
Во-вторых, определение типов идет через =, а не через :.
В-третьих, если уж на то пошло, то объявление тип PChar можно представить так.
_Char=Array [0..0] of Char;
PCHar=^_Char;
Да кто-то может сказать, что при таком объявлении нужно будет писать \"^\" когда нужно будет обращаться к переменной, но в последних версиях Delphi знак \"^\" можно опускать.
При получении указателя на строку возвращается адрес первого символа в строке, значит, такие выражения одинаковы:
Пример 2: Не знаю почему, но вместо двоек выводится не пойми что. Мистика!!!
Никакой мистики.
Просто в дельфи есть два различных типа
- String[число] =ShortString
- String =AnsiString или ShortString
String[число] - это массив, начинающийся с 0 длиной Число+1. В 0 позиции храниться длинна строки.
String-это уже сложная структура.
В программе переменная s:AnsiString ; будет представлена как указатель.
Вернее AnsiString - это нечто наподобие динамический массив, но таковым не является.
Этот тип позволяет работать со строками у которых длина гораздо больше 255 символов.
В паскале String=String[255]
Деректива {$H+} присвает String тип AnsiString
Деректива {$H-} присвает String тип ShortString
код этого символа и есть длинна строки
ord(str[0]) = StrLength(str)
это ещё со времён трупо паскаля так!!! )))