29th
Авг

Загадочный тип PCHAR (delphi)

Posted by Chas under Статьи

    Здравствуйте, дельфисты! Сегодня вам поведую, что это за тип 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. Вот такими словами кончается моя очередная статья.

Руслан Аблязов E-mail

Похожие статьи