Шаг 35.
Пользовательские типы. Строковые типы. Длинные строки AnsiString

    На этом шаге мы рассмотрим правила использования длинных строк.

    В отличие от ShortString-строк память под длинные строки AnsiString выделяется не статически, а динамически. Кроме того, AnsiString-cтpоки не имеют максимальной длины, устанавливаемой при объявлении, а только динамическую текущую длину. Общим для обоих типов строк ляется наличие дескриптора. Однако, если для коротких строк дескриптор имеет простейший вид дополнительного нулевого элемента, в котором хранится текущая длина строки, то у длинных строк дескриптор представляет собой более сложную структуру длиной в восемь байт, которая имеет следующий вид:


Рис.1. Структура дескриптора

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

    Обобщая сказанное, внутреннее строение длинной строки в памяти можно представить так:

var
    LongStr1, LongStr2   :   AnsiString;

    После обработки объявлений значение указателя на строку AnsiString устанавливается в nil:


Рис.2. Результат описания переменной

   

begin
    LongStrl := 'Очень длинная строка...Очень длинная строка';

    После такого присваивания структура представления строки AnsiString примет следующий вид:


Рис.3. Результат присваивания

    Отметим еще несколько особенностей реализации AnsiString-строк. В конец каждой такой строки автоматически добавляется нуль-символ (символ с кодом 0). Этот символ не является необходимым для реализации длинных строк, однако добавляется, чтобы обеспечить возможность преобразования строк AnsiString к строкам с завершающим нулем, которым в Object Pascal соответствует предопределенный тип PChar. При этом преобразования (перезаписывания в новую область памяти) как такового не происходит, а просто строковый указатель типа PChar устанавливается на начало значащих символов AnsiString-строки. И поскольку в конце такой строки нуль-символ уже есть, то работа с переменной типа PChar выполняется корректно. Аналогично допустимо преобразование длинных строк к типу Pointer. Однако, выполняя такие преобразования следует помнить, что изменение значений длинных строк посредством переменных типа PChar или Pointer иногда может привести к непредсказуемым результатам. Поэтому рекомендуется использовать преобразования AnsiString-строк к типам PChar и Pointer только для чтения.

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

  LongStr2   := LongStr1;	
то схема распределения памяти будет иметь вид, показанный на рисунке 4:


Рис.4. Схема распределения памяти

    Таким образом, при выполнении присваивания выполняется копирование только 4х-байтового значения указателя на строку, в результате чего операторы присваивания строк типа AnsiString выполняются значительно быстрее, чем присваивание строк типа ShortString, и оперативная память расходуется экономнее.

    Отметим, что значение счетчика ссылок всегда равняется числу переменных длинного строкового типа, которым соответствует данная строка.

    Если эта строка будет присвоена еще одной переменной LongStr3, то счетчик ссылок станет равным трем. Если после этого какая-либо переменная, например LongStr1, получит новое значение, то счетчик ссылок строки 'Очень длинная строка...Очень длинная строка' будет уменьшен на единицу, а счетчик ссылок новой строки, присвоенной переменной LongStr1, будет увеличен на единицу. Если значения новой строки в памяти еще не существует, то для нее выделяется память в динамической области. Если же после очередного оператора счетчик ссылок становится равным нулю, то эта строка из памяти удаляется.

    Предопределенный идентификатор AnsiString всегда соответствует длинным строкам независимо от значения директивы компилятора .

    Со следующего шага мы начнем знакомиться со структурированными типами.




Предыдущий шаг Содержание Следующий шаг