Этот код выдаст Равно. Как мы знаем из предыдущих примеров (см. листинг 3.22), значения указателей не будут равны, следовательно, производится сравнение по содержанию, т. е. именно то, что к требуется. Если исследовать код, который генерирует компилятор, то можно увидеть, что сначала неявно создается строка AnsiString
, в которую копируется содержимое строки PChar
, а потом сравниваются две строки AnsiString
. Сравниваются, естественно, по значению.
Для строк ShortString
сравнение указателей невозможно, две таких строки всегда сравниваются по значению. Правила хранения литералов и сравнения с другими типами следующие:
1. Литералы типа ShortString
размещаются в сегменте кода только один раз на одну функцию, сколько бы раз они ни повторялись в ее тексте.
2. При сравнении строк ShortString
и AnsiString
первая сначала конвертируется в тип AnsiString
, а потом выполняется сравнение.
3. При сравнении строк ShortString
и PChar
строка PChar
конвертируется в ShortString
, затем эти строки сравниваются.
Последнее правило таит в себе «подводный камень», который иллюстрируется следующим примером (листинг 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) дает правильный результат Не равно.
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).
AnsiString
к PChar
procedure TForm1.Button1 °Click(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).
PChar
procedure TForm1.Button11Click(Sender: TObject);
var
S: string;