А. Григорьев - О чём не пишут в книгах по Delphi
- Название:О чём не пишут в книгах по Delphi
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2008
- Город:СПб
- ISBN:978-5-9775-019003
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.
Для программистов
О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Происходит это вот почему: строка PCharоказывается больше, чем максимально допустимый размер строки ShortString. Поэтому при конвертировании лишние символы просто отбрасываются. Получается строка длиной 255 символов, совпадающая со строкой ShortString, с которой мы ее сравниваем. Отсюда вывод: если строка ShortStringсодержит 255 символов, а строка PChar— более 255 символов, и ее первые 255 символов совпадают с символами строки ShortString, операция сравнения ошибочно даст положительный результат, хотя эти строки не равны.
Избежать этой ошибки поможет либо явное сравнение длины перед сравнением строк, либо приведение одной из сравниваемых строк к типу AnsiString(второй аргумент при этом также будет приведен к этому типу). Следующий пример (листинг 3.27) дает правильный результат Не равно.
ShortStringи PCharprocedure TForm1.Button9Click(Sender: TObject);
var
P: PChar;
S: ShortString;
begin
P := StrAlloc(300);
FillChar(P^, 299, 'A');
P[299] := #0;
S[0] := #255;
FillChar(S[1], 255, 'A');
if string(S) = P then Label1.Caption := 'Равно'
else Label1.Caption := 'He равно';
StrDispose(P);
end;
Учтите, что конвертирование в AnsiString— операция дорогостоящая в смысле процессорного времени (в этом примере будут выделены, а потом освобождены два блока памяти), поэтому там, где нужна производительность, целесообразнее вручную сравнить длину, а еще лучше вообще по возможности избегать сравнения строк разных типов, т.к. без конвертирования это в любом случае не обходится.
Теперь зададимся глупым, на первый взгляд, вопросом: если мы приведем строку AnsiStringк PChar, будут ли равны указатели? Проверим это (листинг 3.28).
AnsiStringк PCharprocedure TForm1.Button10Click(Sender: TObject);
var
S: string;
P: PChar;
begin
S := 'Test';
P := PChar(S);
if Pointer(S) = P then Label1.Caption := 'Равно'
else Label1.Caption := 'Не равно';
end;
Вполне ожидаемый результат — Равно. Можно, например, перенести строку из сегмента кода в динамическую память с помощью UniqueString— результат не изменится. Однако выводы делать рано. Рассмотрим следующий пример (листинг 3.29).
PCharprocedure TForm1.Button11Click(Sender: TObject);
var
S: string;
P: PChar;
begin
S := '';
P := PChar(S);
if Pointer(S) = P then Label1.Caption : = 'Равно'
else Label1.Caption := 'He равно';
end;
От предыдущего он отличается только тем, что строка Sимеет пустое значение. Тем не менее на экране мы увидим Не равно. Связано это с тем, что приведение строки AnsiStringк типу PCharна самом деле не является приведением типов. Это скрытый вызов функции _LStrToPChar, и сделано так для того, чтобы правильно обрабатывать пустые строки.
Значение ''(пустая строка) для строки AnsiStringозначает, что память для нее вообще не выделена, а указатель имеет значение nil. Для типа PCharпустая строка — это ненулевой указатель на символ #0. Нулевой указатель также может рассматриваться как пустая строка, но не всегда — иногда это рассматривается как отсутствие какого бы то ни было значения, даже пустого (аналог NULL в базах данных). Чтобы решить это противоречие, функция _LStrToPCharпроверяет, пустая ли строка хранится в переменной, и, если не пустая, возвращает этот указатель, а если пустая, то возвращает не nil, а указатель на символ #0, который специально для этого размещен в сегменте кода. Таким образом, для пустой строки PChar(S) <> Pointer(S), потому что приведение строки AnsiStringк указателю другого типа — это нормальное приведение типов без дополнительной обработки значения.
3.3.5. Побочное изменение
Из-за того, что две одинаковые строки AnsiStringразделяют одну область памяти, на неожиданные эффекты можно натолкнуться, если модифицировать содержимое строки в обход стандартных механизмов. Следующий код (листинг 3.30, пример SideChange на компакт-диске) иллюстрирует такую ситуацию.
S2при измененииS1procedure TForm1.Button1Click(Sender: TObject);
var
S1, S2: string;
P: PChar;
begin
S1 := 'Test';
UniqueString(S1);
S2 := S1;
P := PChar(S1);
P[0] := 'F';
Label1.Caption := S2;
end;
В этом примере требует комментариев процедура UniqueString. Она обеспечивает то, что счетчик ссылок на строку будет равен единице, т.е. для этой строки делается уникальная копия. Здесь это понадобилось для того, чтобы строка S1хранилась в динамической памяти, а не в сегменте кода, иначе мы получили бы Access violation, как и во втором случае рассмотренного ранее примера Constants (см. листинг 2.17).
В результате работы этого примера на экран будет выведено не Test, a Fest, хотя значение S2, казалось бы, не должно меняться, потому что изменения, которые мы делаем, касаются только S1. Но более внимательный анализ подсказывает объяснение: после присваивания S2 := S1счетчик ссылок строки становится равным двум, а сама строка разделяется двумя указателями: S1и S2. Если бы мы попытались изменить непосредственно S2, то сначала была бы создана копия этой строки, а потом сделаны изменения в этой копии, а оригинал, на который указывала бы S2, остался без изменений. Но, использовав PChar, мы обошли механизм копирования, поэтому строка осталась в единственном экземпляре, и изменения затронули не только S1, но и S2.
В данном примере все достаточно очевидно, но в более сложных случаях разработчик программы может и не подозревать, что строка, с которой он работает, разделяется несколькими переменными. Справка Delphi советует сначала обеспечить уникальность копии строки с помощью UniqueStringи только потом работать с ней через PChar, если в этом есть необходимость.
Рассмотрим еще один пример, практически не отличающийся от предыдущего (листинг 3.31).
S2при изменении S1procedure TForm1.Button2Click(Sender: TObject);
var
S1, S2: string;
P: PChar;
begin
S1 := 'Test';
UniqueString(S1);
S2 := S1;
P := @S1[1];
P[0] := 'F';
Label1.Caption := S2;
end;
В этом случае на экран будет выведено Test, т.е. побочного изменения переменной не произойдёт, хотя переменная S1по прежнему изменяется в обход стандартных механизмов Delphi.
Интервал:
Закладка: