- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
P := PChar(S);
P[0] := 'T'; { * }
Label1.Caption := P;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
S: string;
begin
S := 'Xest';
S[1] := 'T';
Label1.Caption := S;
end;
procedure TForm1.Button4Click(Sender: TObject);
var
S: ShortString;
begin
S := 'Xest';
S[1] := 'T';
Label1.Caption := S;
end;
procedure TForm1.Button5Click(Sender: TObject);
var
S: ShortString;
P: PChar;
begin
S := 'Xest';
P := @S[1];
P[0] := 'T';
Label1.Caption := P;
end;
В этом примере только нажатие на третью и четвертую кнопку приводит к появлению надписи Test. Первые два обработчика вызывают исключение Access violation в строках, отмеченных звездочками, а при нажатии пятой кнопки программа обычно работает без исключении (хотя в некоторых случаях оно все же может возникнуть), но к слову "Test" добавляется какой-то мусор. Разберемся, почему так происходит.
Встретив в первом обработчике литерал 'Xest' и определив, что он относится к типу PChar, компилятор выделяет в подходящей области сегмента кода пять байтов (четыре значащих символа и один завершающий ноль), а в указатель P заносится адрес этого литерала. Сегмент кода доступен только для чтения, прав на его изменение система программе в целях безопасности не дает, поэтому попытка изменить то, что находится в этом сегменте, приводит к закономерному результату — выдаче сообщения "Access violation".
В обработчике второй кнопки происходит почти то же самое, с той лишь разницей. что для литерала выделяется на восемь байтов больше: т.к. в данном случае литерал имеет тип AnsiString, ему нужны еще 4 байта для хранения длины и 4 — для счетчика ссылок. В переменную S записывается указатель на этот литерал. Приводя эту переменную к типу PChar, мы, по сути, просто копируем этот указатель в переменную P, а дальше происходит то же самое — попытка изменить страницу памяти, доступную программе только для чтения с тем же самым результатом.
В третьем случае литерал, как и раньше, размещается в сегменте кода. Счетчик ссылок у таких литералов всегда равен -1 — это значение указывает менеджеру памяти, что это константа, которая не может быть изменена и память для которой не нужно освобождать. Поэтому при любой попытке изменить переменную, которой присвоен литерал, срабатывает механизм копирования по необходимости: для строки выделяется место в динамической памяти, затем значение литерала копируется в эту область, обновляется значение указателя S, а затем выполняется изменение копии, находящейся в динамической памяти. Так как эта память доступна и для чтения, и для записи, исключение не возникает, и все работает так, как и было задумано.
В четвертом случае литерал также хранится в сегменте кода, но работы с указателем уже нет. Этот литерал занимает там пять байтов: один байт на длину и четыре — на символы. Переменная 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. Приведение литералов к типу PCharprocedure 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. В нем одиннадцать кнопок, и обработчик каждой из них иллюстрирует одну из возможных ситуаций.