- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
Начнем со сравнения двух строк типа PChar (листинг. 3.19).
Листинг 3.19. Сравнение строк типа PCharprocedure TForm1.Button1Click(Sender: TObject);
var
P1, P2: PChar;
begin
P1 := StrNew('Test');
P2 := StrNew('Test');
if P1 = P2 then Label1.Caption := 'Равно';
else Label1.Caption := 'Не равно';
StrDispose(P1);
StrDispose(P2);
end;
В данном примере мы увидим надпись Не равно. Это происходит потому, что в этом случае сравниваются указатели, а не содержимое строк, а указатели здесь будут разные. Попытка сравнить строки с помощью оператора сравнения — весьма распространенная ошибка у начинающих. Для сравнения таких строк следует применять специальную функцию — 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. Сравнение глобальных переменных типа PCharvar
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 и PCharprocedure 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 и PCharprocedure 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) дает правильный результат Не равно.

