Social Icons

пятница, 18 сентября 2009 г.

Строки в Delphi

Данный пост представляет собой подборку статей по работе со строками в Delphi...

Собственно, вот они:
1) Тонкости работы со строками
2) PChars: сами строки не включены

Небольшое вступление из первой статьи:

Виды строк в Delphi
Для работы с кодировкой ANSI в Delphi существует три вида строк — AnsiString, ShortString и PChar. Между собой они различаются тем, где хранится строка и как выделяется и освобождается память для неё. Зарезервированное слово string по умолчанию означает тип AnsiString, но если после неё стоит число в квадратных скобках, то это означает тип ShortString, а число — ограничение по длине. Кроме того, существует опция компилятора Huge strings (управляется также директивами компилятора {$H+/-} и {$LONGSTRINGS ON/OFF}), которая по умолчанию включена, но если её выключить, то слово string станет эквивалентно ShortString или, что то же самое, string[255]. Эта опция введена для обратной совместимости с Turbo Pascal, в новых программах отключать её нет нужды.

Наиболее просто устроен тип ShortString. Это — массив символов с индексами от 0 до N, где N — число символов, указанное при объявлении переменной (в случае использования идентификатора ShortString N явно не указывается и равно 255). Нулевой элемент массива хранит текущую длину строки, которая может быть меньше или равна объявленной (эту длину мы будем далее обозначать M), элементы с индексами от 1 до M — символы, составляющие строку. Значения элементов с индексами M+1..N не определены. Все стандартные функции для работы со строками игнорируют эти символы. В памяти такая переменная всегда занимает N+1 байт.

Ограничения типа ShortString очевидны: так как на хранение длины отводится только один байт, такая строка не может содержать больше 255-ти символов. Кроме того, такой способ записи длины не совпадает с принятым в Windows, поэтому ShortString несовместим с системными строками.

В системе приняты так называемые нуль-терминированные строки: строка передаётся указателем на её первый символ, длина строки отдельно нигде не хранится, признаком конца строки считается встретившийся в цепочке символов #0. Длина таких строк ограничена только доступной памятью и способом адресации (т.е. в Windows это 4294967295 символов). Для работы с такими строками предусмотрен тип PChar. Переменная такого типа является указателем на начало строки. В литературе нередко можно встретить утверждение, что PChar=^Char, однако это неверно: тип PChar встроен в компилятор и не выводится из других типов. Это позволяет выполнять с ним операции, недопустимые для других указателей. Во-первых, если P — переменная типа PChar, то допустимо обращение к отдельным символам строки с помощью конструкции P[N], где N — целочисленное выражение, определяющее номер символа (в отличие от типа ShortString, здесь символы нумеруются с 0, а не с 1). Во-вторых, к указателям типа PChar разрешено добавлять и вычитать целые числа, смещая указатель на соответствующее количество байт вверх или вниз (здесь речь идёт только об использовании операторов "+" и "-"; адресная арифметика с помощью процедур Inc и Dec доступна для любых типизированных указателей, а не только для PChar).

При использовании PChar программист целиком и полностью отвечает за выделение памяти для строки и за её освобождение. Именно это и служит основным источником ошибок у новичков: они пытаются работать с такими строками так же, как и с AnsiString, надеясь, что операции с памятью будут выполнены автоматически. Это очень грубая ошибка, способная привести к самым непредсказуемым последствиям.

Хотя программист имеет полную свободу выбора в том, как именно выделять и освобождать память для нуль-терминированных строк, в большинстве случаев самыми удобными оказываются специально предназначенные для этого функции StrNew, StrDispose и т.п. Их удобство заключается в том, что менеджер памяти выделяет памяти чуть больше, чем требуется для хранения строки, и в эту дополнительную память записывается, сколько байт было выделено. Благодаря этому функция StrDispose удаляет ровно столько памяти, сколько было выделено, даже если в середину выделенного блока был записан символ #0, уменьшающий длину строки.

Компилятор также позволяет рассматривать статические массивы типа Char, начинающиеся с нулевого индекса, как нуль-терминированные строки. Такие массивы совместимы с типом PChar, что позволяет обойтись без использования динамической памяти при работе со строками.

Тип AnsiString объединяет достоинства типов ShortString и PChar: строки имеют фактически неограниченную длину, заботиться о выделении памяти для них не нужно, в их конец автоматически добавляется символ #0, что делает их совместимыми с системными строками (впрочем, эта совместимость не абсолютная).

Переменная типа AnsiString — это указатель на первый символ строки, как и в случае PChar. Разница в том, что перед этой строкой в память записывается дополнительная информация: длина строки и счётчик ссылок. Это позволяет компилятору генерировать код, автоматически выделяющий, перераспределяющий и освобождающий память, выделяемую для строки. Работа с памятью происходит совершенно прозрачно для программиста, в большинстве случаев со строками AnsiString можно работать, вообще не задумываясь об их внутреннем устройстве. Символы в таких строках нумеруются с единицы, чтобы облегчить перенос старых программ, использовавших ShortString.

Счётчик ссылок позволяет реализовать то, что называется copy-on-demand, копирование по необходимости. Если у нас есть две переменные S1, S1 типа AnsiString, присваивание вида S1 := S2 не приводит к копированию всей строки. Вместо этого в указатель S1 копируется значение указателя S2, а счётчик ссылок строки увеличивается на единицу. В дальнейшем, если одну из этих строк потребуется изменить, она сначала будет скопирована (а счётчик ссылок оригинала, естественно, уменьшен) и только потом изменена, чтобы это изменение не затрагивало остальные переменные.

Продолжение по ссылкам, указанным в начале страницы.

Комментариев нет:

Отправить комментарий

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

 

Подписчики

Статистика