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

на главную

Жанры

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

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

Шрифт:

В четвертом случае литерал также хранится в сегменте кода, но работы с указателем уже нет. Этот литерал занимает там пять байтов: один байт на длину и четыре — на символы. Переменная

S
размешается в стеке, занимая там 256 байтов, а присваивание ей литерала — это копирование значения литерала из сегмента кода в область памяти, занятую переменной. Таким образом, в дальнейшем мы работаем не с константой в сегменте кода, а с ее копией в стеке, которую можно без проблем модифицировать.

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

P
адреса первого символа строки приходится использовать оператор получения адреса
@
. Модификация
строки проходит, как и в предыдущем случае, успешно, но при присваивании выражения типа
PChar
свойству типа
AnsiString
длина строки определяется по правилам, принятым для
PChar
, т.е. строка сканируется до обнаружения нулевого символа. Но
поскольку
ShortString "не отвечает" за то, что будет содержаться в неиспользуемых символах, там может остаться всякий мусор от предыдущего использования стека. Никакой гарантии, что сразу после последнего символа будет
#0
, нет. Отсюда и появление непонятных символов на экране.

Общий вывод таков: пока мы не вмешиваемся в работу компилятора с типами

ShortString
и
AnsiString
, получаем ожидаемый результат. Работа с этими же строками через
PChar
в обход стандартных механизмов приводит к появлению проблем. Кроме того, при работе со строками
PChar
необходимо четко представлять, где и как выделяется для них память, иначе можно получить неожиданную ошибку.

3.3.3. Приведение литералов к типу PChar

В разд. 1.1.13 мы уже говорили, что когда у функции есть параметр типа

PChar
, и этот параметр не будет изменяться функцией, при вызове ей можно передавать строковый литерал (см. листинг 1.20). Компилятор размещает литерал в сегменте кода и передает функции указатель на эту память.

В примерах кода, приведенных на различных сайтах, можно нередко встретить такую ситуацию, когда литерал, передаваемый в качестве параметра типа

PChar
, явно приводится к этому типу. Разберемся, что это дает. Для этого положим на форму четыре кнопки и напишем в обработчиках их нажатия следующий код (листинг 3.18. пример
PCharLit
на компакт-диске).

Листинг 3.18. Приведение литералов к типу
PChar

procedure TForm1.Button1Click(Sender: TObject);

begin

 Application.MessageBox('Text', nil, 0);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

 Application.MessageBox('A', nil, 0);

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

 Application.MessageBox(PChar('Text'), nil, 0);

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

 Application.MessageBox(PChar('A'), nil, 0);

end;

Метод

TApplication.MessageBox
по каким-то непонятным причинам имеет параметры типа
PChar
вместо
string
, и мы этим воспользуемся. При его вызове будет показано диалоговое окно с текстом, переданным в качестве первого параметра (в заголовке будет написано Ошибка, т.к. второй параметр у нас
nil
). Нажатие на первую и вторую кнопку не приводит ни к каким неожиданностям —
мы видим на экране Text и А соответственно. Теперь перейдем к коду с явным приведением литерала к
PChar
. Нажатие на третью кнопку к сюрпризам не приведет, а вот нажатие на четвертую даст исключение Access violation.

Происходит это потому, что тип литерала зависит не только от его вида, но и оттого, в каком контексте он упомянут. Например, в предыдущем разделе мы видели, что литерал

'Xest'
мог иметь тип
string
или
PChar
в зависимости от того, какой переменной он присваивался. Там, где явного приведения типов нет, тип литерала однозначно определяется по типу формального параметра, и в обработчиках нажатия первых двух кнопок компилятор создает правильные литералы
'Text'
и
'А'
типа
PChar
. Явное приведение литерала к типу
PChar
меняет контекст, в котором литерал упомянут, и компилятор может сделать неправильный вывод о его типе. В обработчике третьей кнопки компилятор правильно понимает, что литерал имеет тип
PChar
и генерирует код, полностью эквивалентный коду обработчика первой кнопки. А вот в случае приведения к типу
PChar
литерала
'А'
компилятор принимает этот литерал не за строковый, а за символьный (т.е. за литерал типа
Char
), состоящий из одного символа без всяких добавлений длины, символа
#0
и т.п. При приведении выражения типа
Char
к любому указателю (в том числе и к
PChar
) оно рассматривается как выражение любого порядкового типа, и его численное значение становится численным значением указателя. В нашем случае это символ с кодом 65 ($41 в шестнадцатиричной записи), поэтому в функцию передается указатель $00000041. Такой указатель указывает на ту область виртуальной памяти, которая никогда не отображается на физическую память, поэтому его использование приводит к ошибке Access violation.

Итак, мы увидели, что явное приведение литерала к типу

PChar
либо никак не отражается на генерируемом компилятором коде (в случае литералов из нескольких символов), либо приводит к генерированию заведомо некорректного кода (в случае односимвольных литералов). Если еще учесть, что приведение литералов к
PChar
загромождает код, легко сделать вывод, что приводить литералы к
PChar
не нужно, поскольку это потенциальный источник проблем и признак плохого оформления кода. 

3.3.4. Сравнение строк

Для типов

PChar
и
AnsiString
, которые являются указателями, понятие равенства двух строк может толковаться двояко: либо как равенство указателей, либо как равенство содержимого памяти, на которую эти указатели указывают. Второй вариант предпочтительнее, т.к. он ближе к интуитивному понятию равенства строк. Для типа
AnsiString
реализован именно этот вариант, т.е. сравнивать такие строки можно, ни о чем не задумываясь. Более сложные ситуации мы проиллюстрируем примером Companions. В нем одиннадцать кнопок, и обработчик каждой из них иллюстрирует одну из возможных ситуаций.

Начнем со сравнения двух строк типа

PChar
(листинг. 3.19).

Листинг 3.19. Сравнение строк типа
PChar

procedure TForm1.Button1Click(Sender: TObject);

var

 P1, P2: PChar;

begin

 P1 := StrNew('Test');

 P2 := StrNew('Test');

 if P1 = P2 then Label1.Caption := 'Равно';

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

Поделиться:
Популярные книги

Корсар

Русич Антон
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
6.29
рейтинг книги
Корсар

Девяностые приближаются

Иванов Дмитрий
3. Девяностые
Фантастика:
попаданцы
альтернативная история
7.33
рейтинг книги
Девяностые приближаются

Жена фаворита королевы. Посмешище двора

Семина Дия
Фантастика:
фэнтези
5.00
рейтинг книги
Жена фаворита королевы. Посмешище двора

(не)Бальмануг. Дочь 2

Лашина Полина
8. Мир Десяти
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
(не)Бальмануг. Дочь 2

Буря империи

Сай Ярослав
6. Медорфенов
Фантастика:
аниме
фэнтези
фантастика: прочее
эпическая фантастика
5.00
рейтинг книги
Буря империи

Идеальный мир для Лекаря 13

Сапфир Олег
13. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 13

Книга пятая: Древний

Злобин Михаил
5. О чем молчат могилы
Фантастика:
фэнтези
городское фэнтези
мистика
7.68
рейтинг книги
Книга пятая: Древний

Зеркало силы

Кас Маркус
3. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Зеркало силы

Имя нам Легион. Том 5

Дорничев Дмитрий
5. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 5

Аномальный наследник. Том 3

Тарс Элиан
2. Аномальный наследник
Фантастика:
фэнтези
7.74
рейтинг книги
Аномальный наследник. Том 3

Архил...? 4

Кожевников Павел
4. Архил...?
Фантастика:
фэнтези
попаданцы
альтернативная история
5.50
рейтинг книги
Архил...? 4

Хозяйка лавандовой долины

Скор Элен
2. Хозяйка своей судьбы
Любовные романы:
любовно-фантастические романы
6.25
рейтинг книги
Хозяйка лавандовой долины

Отмороженный 10.0

Гарцевич Евгений Александрович
10. Отмороженный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Отмороженный 10.0

Ваше Сиятельство 2

Моури Эрли
2. Ваше Сиятельство
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Ваше Сиятельство 2