Чтение онлайн

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

Этот пример отличается от приведенного в листинге 3.21 только тем, что теперь переменные глобальные, а не локальные. Однако этого достаточно, чтобы результат оказался другим — на экране мы увидим надпись Не равно. Для глобальных переменных компилятор всегда создаст уникальный литерал, на обнаружение одинаковых литералов ему "интеллекта" не хватает. Более того, если поставить точки останова в методах

Button3Click
и
Button4Click
, легко убедиться, что указатель, который будет помещен в переменную 
S
в методе
Button4Click
, отличается от того, который будет помещен в переменные
S1
и
S2
в методе
Button3Click
,
хотя литералы в обоих случаях одинаковые. Компилятор умеет обнаруживать равенство литералов типа
AnsiString
только в пределах одной функции.

Теперь посмотрим, что будет с глобальными переменными типа

PChar
при присваивании им одинакового литерала (листинг 3.24).

Листинг 3.24. Сравнение глобальных переменных типа
PChar

var

 GP1, GP2: PChar;

procedure TForm1.Button6Click(Sender: TObject);

begin

 GP1 := 'Test';

 GP2 := 'Test';

 if GP1 = GP2 then Label1.Caption := 'Равно'

 else Label1.Caption := 'He равно';

end;

После выполнения этого кода мы увидим надпись Равно, т.е. здесь компилятор смог обнаружить равенство литералов, несмотря на то, что переменные глобальные. Однако переменные типа

PChar
, которым присваиваются одинаковые литералы в разных функциях, как и переменные типа
AnsiString
, получат разные значения.

Но вернемся к сравнению строк. Как мы знаем, строки

AnsiString
сравниваются по значению, а
PChar
— по указателю. А что будет, если сравнить
AnsiString
с
PChar
? Ответ на этот вопрос даёт листинг 3.25.

Листинг 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;

Этот код выдаст Равно. Как мы знаем из предыдущих примеров (см. листинг 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';

Поделиться с друзьями: