О чём не пишут в книгах по Delphi
Шрифт:
Листинг 3.25. Сравнение переменных типа
AnsiString
и PChar
procedure TForm1.Button7Click(Sender: TObject);
var
P: PChar;
S: string;
begin
S := 'Test';
P := 'Тest';
it S = Р then Label1.Caption := 'Равно'
else Label1.Caption := 'Не равно';
end;
Этот код выдаст Равно. Как мы знаем
AnsiString
, в которую копируется содержимое строки PChar
, а потом сравниваются две строки AnsiString
. Сравниваются, естественно, по значению. Для строк
ShortString
сравнение указателей невозможно, две таких строки всегда сравниваются по значению. Правила хранения литералов и сравнения с другими типами следующие: 1. Литералы типа
ShortString
размещаются в сегменте кода только один раз на одну функцию, сколько бы раз они ни повторялись в ее тексте. 2. При сравнении строк
ShortString
и AnsiString
первая сначала конвертируется в тип AnsiString
, а потом выполняется сравнение. 3. При сравнении строк
ShortString
и PChar
строка PChar
конвертируется в ShortString
, затем эти строки сравниваются. Последнее правило таит в себе «подводный камень», который иллюстрируется следующим примером (листинг 3.26).
Листинг 3.26. Ошибка при сравнении переменных типа
ShortString
и PChar
procedure TForm1.Button8Click(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 S = P then Label1.Caption := 'Равно'
else Label1.Caption := 'Не равно';
StrDispose(Р);
end;
Здесь формируется строка типа
PChar
, состоящая из 299 символов "A". Затем формируется строка ShortString
, состоящая из 255 символов "А". Очевидно, что эти строки не равны, потому что имеют разную длину. Тем не менее на экране появится надпись Равно. Происходит это вот почему: строка
PChar
оказывается больше, чем максимально допустимый размер строки ShortString
. Поэтому при конвертировании лишние символы просто отбрасываются. Получается строка длиной 255 символов, совпадающая со строкой ShortString
, с которой мы ее сравниваем. Отсюда вывод: если строка ShortString
содержит 255 символов, а строка PChar
— более 255 символов, и ее первые 255 символов совпадают с символами строки ShortString
, операция сравнения ошибочно даст положительный результат, хотя эти строки не равны. Избежать этой ошибки поможет либо явное сравнение длины перед сравнением строк, либо приведение одной из сравниваемых строк к
AnsiString
(второй аргумент при этом также будет приведен к этому типу). Следующий пример (листинг 3.27) дает правильный результат Не равно. Листинг 3.27. Правильное сравнение переменных типа
ShortString
и PChar
procedure 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). Листинг 3.28. Равенство указателей после приведения
AnsiString
к PChar
procedure 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). Листинг 3.29. Сравнение указателя после приведения пустой строки к
PChar
procedure 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
, и сделано так для того, чтобы правильно обрабатывать пустые строки.
Поделиться:
Популярные книги
Долгий путь домой
Вселенная EVE Online
Фантастика:
космическая фантастика
попаданцы
6.20
рейтинг книги
Убивать чтобы жить 6
6. УЧЖ
Фантастика:
боевая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
Возвышение Меркурия. Книга 3
3. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Фиктивная жена
1. Братья Вертинские
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Здравствуй, 1985-й
2. Девяностые
Фантастика:
альтернативная история
5.25
рейтинг книги
Барон Дубов 4
4. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Лекарь для захватчика
Фантастика:
попаданцы
историческое фэнтези
фэнтези
5.00
рейтинг книги
Идеальный мир для Лекаря 24
24. Лекарь
Фантастика:
городское фэнтези
попаданцы
5.00
рейтинг книги
Ваше Сиятельство 9
9. Ваше Сиятельство
Фантастика:
боевая фантастика
попаданцы
стимпанк
аниме
фэнтези
5.00
рейтинг книги
Господин следователь
1. Господин следователь
Детективы:
исторические детективы
5.00
рейтинг книги
Академия проклятий. Книги 1 - 7
Академия Проклятий
Фантастика:
фэнтези
8.98
рейтинг книги
Полковник Гуров. Компиляция (сборник)
Полковник Гуров
Детективы:
криминальные детективы
шпионские детективы
полицейские детективы
боевики
крутой детектив
5.00
рейтинг книги
Первый среди равных. Книга V
5. Первый среди Равных
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Землянка для двух нагов
Фантастика:
космическая фантастика
5.00