О чём не пишут в книгах по 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
, и сделано так для того, чтобы правильно обрабатывать пустые строки.
Поделиться:
Популярные книги
Белые погоны
3. Гибрид
Фантастика:
фэнтези
попаданцы
технофэнтези
аниме
5.00
рейтинг книги
Черный дембель. Часть 1
1. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Лишняя дочь
Любовные романы:
любовно-фантастические романы
8.22
рейтинг книги
Темный Лекарь 5
5. Темный Лекарь
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Последний из рода Демидовых
Фантастика:
детективная фантастика
попаданцы
аниме
5.00
рейтинг книги
Чиновникъ Особых поручений
6. Александр Агренев
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Попаданка в академии драконов 4
4. Попаданка в академии драконов
Любовные романы:
любовно-фантастические романы
7.47
рейтинг книги
Боги, пиво и дурак. Том 6
6. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Курсант: Назад в СССР 10
10. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Сделай это со мной снова
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Болотник 2
2. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Камень Книга двенадцатая
12. Камень
Фантастика:
боевая фантастика
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Небо для Беса
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Надуй щеки! Том 4
4. Чеболь за партой
Фантастика:
попаданцы
уся
дорама
5.00