Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Загадочный тип 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. Вот такими словами кончается моя очередная статья.

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
95K
22 марта 2015 года
ya.qwe99
0 / / 22.03.2015
+1 / -0
Мне нравитсяМне не нравится
22 марта 2015, 22:57:07
>> Type
>> 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-го байта структуры данных
2.
90K
17 марта 2013 года
Максим Щербань
0 / / 17.03.2013
+0 / -2
Мне нравитсяМне не нравится
17 марта 2013, 02:18:52
Да это вообще школьник какой-то писал, посмотрите на стиль изложения и грамматические ошибки.
3.
87K
17 ноября 2012 года
Pavel Makaruha
0 / / 17.11.2012
+0 / -1
Мне нравитсяМне не нравится
17 ноября 2012, 04:31:25
отличная статейка !!!
4.
58K
22 февраля 2010 года
O.Nick
0 / / 22.02.2010
Мне нравитсяМне не нравится
29 июня 2011, 14:21:00
Странная статья. Название "Загадочный тип PCHAR", а идет критика String.
Подмена понятий какая-то. В строки с нормальным управлением памятью лезут с, по сути, поитером.
Вы б еще в языках со сборкой мусора с неуправляемыми поинтерами влезли, и рассуждали о том, что, GC плохо ибо "Изначально казалось, что тип string проще и лучше, а получилось как всегда! Тип string принёс нам массу неприятностей".
Плюс ошибки абсолютно не понятные.
5.
261
03 ноября 2005 года
ahilles
1.5K / / 03.11.2005
Мне нравитсяМне не нравится
25 октября 2006, 20:16:13
Type
PCHAR: ^ CHAR;// следовательно также и тип WideChar
PWIDECHAR: ^ WIDECHAR;

оБшибся, оБшибся..... спасибо за резензию статьи
6.
261
03 ноября 2005 года
ahilles
1.5K / / 03.11.2005
+0 / -2
Мне нравитсяМне не нравится
25 октября 2006, 20:13:33
я просто пояснил чайникам что за тип PCHAR и всё.
и привел несколько примеров для лучшего понимания!!!
7.
355
21 октября 2006 года
<SCORP>
786 / / 21.10.2006
+1 / -0
Мне нравитсяМне не нравится
24 октября 2006, 11:48:57
проблема в статье абсолютно надуманая. нет ничего особо загадочного в этом типе . просто немного непривычно для людей, которые раньше писали только с использованием дельфовых классов, а теперь начали напрямую использовать WinAPI
8.
551
22 апреля 2004 года
Pavia
357 / / 22.04.2004
+1 / -0
Мне нравитсяМне не нравится
22 октября 2006, 01:48:22
Типа Srring не существует.

Цитата:
Обращаться к 55 символу переменной str2 уже нельзя, но физически в памяти он существует

Не корректно, к 55 символу мы можем обратиться. Вот только мы выйдем за границу. А следовательно физически полученный нами символ не будет принадлежать строке.

Код:
i:=55;

   caption:=Str2[ i ];

А это вообще шедевр.

Цитата:

Код:
Type

   PCHAR: ^ CHAR;// следовательно также и тип WideChar

   PWIDECHAR: ^ WIDECHAR;


Во-первых, тип PChar нигде не объявляется, а является внутренним, системным.
Во-вторых, определение типов идет через =, а не через :.
В-третьих, если уж на то пошло, то объявление тип PChar можно представить так.

Код:
type

  _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

9.
355
21 октября 2006 года
<SCORP>
786 / / 21.10.2006
+1 / -0
Мне нравитсяМне не нравится
22 октября 2006, 00:02:12
"В сообщении перед двойкой стоит какой то символ"
код этого символа и есть длинна строки
ord(str[0]) = StrLength(str)
это ещё со времён трупо паскаля так!!! )))
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог