Читаем О чём не пишут в книгах по Delphi полностью

Этот код выдаст Равно. Как мы знаем из предыдущих примеров (см. листинг 3.22), значения указателей не будут равны, следовательно, производится сравнение по содержанию, т.е. именно то, что к требуется. Если исследовать код, который генерирует компилятор, то можно увидеть, что сначала неявно создается строка 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

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже