О чём не пишут в книгах по 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
, и сделано так для того, чтобы правильно обрабатывать пустые строки.
Поделиться:
Популярные книги
Ваше Сиятельство
1. Ваше Сиятельство
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Неудержимый. Книга XIV
14. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря
1. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Подаренная чёрному дракону
Любовные романы:
любовно-фантастические романы
7.07
рейтинг книги
Я Гордый часть 2
2. Стальные яйца
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бальмануг. Невеста
5. Мир Десяти
Фантастика:
юмористическое фэнтези
5.00
рейтинг книги
Пограничная река. (Тетралогия)
Пограничная река
Фантастика:
фэнтези
боевая фантастика
9.13
рейтинг книги
Сонный лекарь 6
6. Сонный лекарь
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Тринадцатый
1. Видящий смерть
Фантастика:
фэнтези
попаданцы
аниме
6.80
рейтинг книги
Ночь со зверем
3. Оборотни-медведи
Любовные романы:
любовно-фантастические романы
5.25
рейтинг книги
Страж. Тетралогия
Страж
Фантастика:
фэнтези
9.11
рейтинг книги
Кодекс Охотника. Книга XIV
14. Кодекс Охотника
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том VIII
8. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон устанавливает правила
6. Закон сильного
Старинная литература:
прочая старинная литература
5.00