В данном примере мы увидим надпись Не равно. Это происходит потому, что в этом случае сравниваются указатели, а не содержимое строк, а указатели здесь будут разные. Попытка сравнить строки с помощью оператора сравнения — весьма распространенная ошибка у начинающих. Для сравнения таких строк следует применять специальную функцию —
StrComp
. Следующий пример, на первый взгляд, в плане сравнения ничем не отличается от только что рассмотренного (листинг 3.20).
Листинг 3.20. Сравнение строк
типа
PChar
, заданных одинаковыми литералами
procedure TForm1.Button2Click(Sender: TObject);
var
P1, P2: PChar;
begin
P1 := 'Test';
P2 := 'Test';
if P1 = P2 then Label1.Caption := 'Равно'
else Label1.Caption := 'Не равно';
end;
Разница только в том, что строки хранятся не в динамической памяти, a в сегменте кода. Тем не менее на экране появится надпись Равно. Это происходит, разумеется, не потому, что сравнивается содержимое строк, а потому, что в данном случае два указателя оказываются равными. Компилятор поступает достаточно интеллектуально: видя, что в разных местах указаны литералы с одинаковым значением, он выделяет для такого литерала место только один раз, а потом помещает в разные указатели один адрес. Поэтому сравнение дает правильный (с интуитивной точки зрения) результат.
Такое положение дел только запутывает ситуацию со сравнением
PChar
: написав подобный тест, человек может сделать вывод, что строки
PChar
сравниваются не по указателю, а по значению, и действовать под руководством этого заблуждения.
Раз уж мы столкнулись с такой особенностью компилятора, немного отвлечемся от сравнения строк и "копнем" этот вопрос немного глубже. В частности, выясним, распространяется ли "интеллект" компилятора на литералы типа
AnsiString
(листинг 3.21).
Листинг 3.21. Сравнение переменных типа
AnsiString
как указателей
procedure TForm1.Button3Click(Sender: TObject);
var
S1, S2: string;
begin
S1 := 'Test';
S2 := 'Test';
if Pointer(S1) = Pointer(S2) then Label1.Caption := 'Равно'
else Label1.Caption := 'He равно';
end;
В этом примере на экран будет выведено Равно. Как мы видим, указатели равны, т.е. и здесь компилятор проявил "интеллект".
Рассмотрим чуть более сложный случай (листинг 3.22).
Листинг 3.22. Сравнение переменных
AnsiString
и
PChar
как указателей
procedure TForm1.Button4Click(Sender: TObject);
var
P: PChar;
S: string;
var
S := 'Test';
P := 'Test';
if Pointer(S) = P then Label1.Caption := 'Равно'
else Label1.Caption := 'He равно';
end;
В этом случае указатели окажутся не равны. Действительно,
с формальной точки зрения литерал типа
AnsiString
отличается от литерала типа
PChar
: в нем есть счетчик ссылок (равный -1) и длина. Однако если забыть с существовании этой добавки, эти два литерала одинаковы: четыре значащих символа и один
#0
, т.е. компилятор, в принципе, мог бы обойтись одним литералом. Тем не менее на это ему "интеллекта" уже не хватило. Рассмотрим еще один пример: сравнение строк по указателям (листинг 3.23).
Листинг 3.23. Сравнение глобальных переменных типа
AnsiString
как указателей
var
GS1, GS2: string;
procedure TForm1.Button5Click(Sender: TObject);
begin
GS1 := 'Test';
GS2 := 'Test';
if Pointer(GS1) = Pointer(GS2) then Label1.Caption := 'Равно';
else Label1.Caption := 'Не равно';
end;
Этот пример отличается от приведенного в листинге 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
, получат разные значения.
Но вернемся к сравнению строк. Как мы знаем, строки