7.3. СТРОКИ
Для обработки текстов в Object Pascal используются следующие типы:
Общим для этих типов является то, что каждая строка трактуется как одномерный массив символов, количество символов в котором может меняться в работающей программе: для string [n] длина строки меняется от 0 до n, для string и pchar - от 0 до 2 Гбайт.
В стандартном Паскале используются только короткие строки String [n] . В памяти такой строке выделяется n+i байт, первый байт содержит текущую длину строки, а сами символы располагаются начиная со 2-го по счету байта. Поскольку для длины строки в этом случае отводится один байт, максимальная длина короткой строки не может превышать 255 символов. Для объявления короткой строки максимальной длины предназначен стандартный тип ShortString (эквивалент String[255]).
В Windows широко используются нуль-терминальные строки, представляющие собой цепочки символов, ограниченные символом #о. Максимальная длина такой строки лимитируется только доступной памятью и может быть очень большой.
В 32-разрядных версиях Delphi введен новый тип string, сочетающий в себе удобства обоих типов. При работе с этим типом память выделяется по мере надобности (динамически) и ограничена имеющейся в распоряжении программы доступной памятью. Замечу, что в Delphi 1 тип string эквивалентен String [2 55], т. е. определяет короткую строку максимально возможной длины.
Для совместимости с компонентами, основывающимися на OLE-технологии, в Delphi 32 введены также широкие строки, объявляемые стандартным типом wideString. По своим свойствам они идентичны длинным строкам string, но отличаются от них тем, что для представления каждого символа используются не один, а два байта.
Примеры объявлений строковых типов:
var
ssS: String[250];// Короткая строка длиной до 250 символов
ssMax: ShortString;// Короткая строка длиной до 255 символов
stS : String; // Длинная строка
swS: WideString;// Широкая строка
pcS: PChar; // Ссылка на нуль-терминальную строку
acS: array [0..1000] of Char; // Нуль-терминальная строка
// длиной до 1000 символов
При объявлении переменной sss компилятор выделит для ее размещения 250 + 1 = 251 байт и поместит в первый байт 0 - текущую длину строки. При выполнении такого фрагмента программы:
procedure TfmExample.bbRunClick(Sender: TObject);
var
ssS: String[250];
begin
ssS :='Строка символов';
ssS[6] := ' и'; // Символы в строке нумеруются, начиная с 1
IbOutput.Caption := ssS; // Выводится “Строки символов”
end;
сначала в переменную ssS будет помещена цепочка символов строка символов, причем 1-й байт получит значение 15 (количество символов в строке). После выполнения второго оператора символ с индексом б (индексация байтов начинается с 0, но, поскольку первый байт содержит текущую длину, первый символ в строке имеет индекс 1) будет заменен на “и”, и в переменной окажется цепочка строки символов.
Совершенно другим будет механизм работы с памятью при объявлении длинной строки sts: компилятор выделит для переменной 4 байта, достаточные для размещения номера той ячейки памяти, начиная с которой будет фактически располагаться символьная строка. Говорят, что sts ссылается на строку. Такого рода переменные-ссылки называются указателями и обсуждаются в гл. 9. При выполнении первого оператора такого обработчика:
procedure TfmExample.bbRunClick(Sender: TObject);
var
stS, stSS: String;
begin
stS :='Строка символов';
stSS := stS;
stS := 'Это - '+stS;
stS[7] := ' c' ; // Символы в строке нумеруются, начиная с 1
IbOutput.Caption := stS; //Выводится “Это - строка символов”
end;
программа (а не компилятор!) определит длину цепочки символов Строка символов, обратится к ядру перационной системы (ОС) с требованием выделить для нее участок памяти длиной 15+5=20 байт, поместит в переменную sts номер первого выделенного байта [ На самом деле в stS запоминается дескриптор выделенного участка памяти, см. гл. 9. ] и, начиная с него, разместит в этом участке цепочку символов, завершив ее терминальным нулем и 4-байтным счетчиком ссылок. Такое размещение на этапе прогона программы называется динамическим, в то время как размещение на этапе компиляции - статическим. Счетчик ссылок играет важную роль в механизме работы с памятью. С его помощью реализуется “кэширование” памяти: при выполнении оператора
stSS := stS;
память для размещения значения переменной stSS не выделяется, в переменную stSS помещается содержимое указателя sts, а счетчик ссылок в связанной с ним памяти увеличивается на единицу. Таким образом, оба указателя будут ссылаться на одну и ту же область памяти, счетчик ссылок которой будет содержать значение 2. При выполнении оператора
stS := 'Это - '+stS;
счетчик ссылок уменьшается на единицу, выделяется новая область памяти длиной 2 о + б = 2 б байт, указатель на эту область помещается в stS, а в саму память переписывается цепочка символов Это -строка символов, терминальный ноль и содержащий единицу счетчик ссылок. Теперь переменные stS и stss будут ссылаться на разные участки памяти, счетчики ссылок которых будут содержать по единице. Выделенная для размещения строки String область памяти освобождается, если ее счетчик ссылок стал равен нулю.
Похожим образом осуществляется работа с памятью при объявлении переменной pcs типа pchar: компилятор считает эту переменную указателем и выделит для нее 4 байта:
procedure TfmExample.bbRunClick(Sender: TObject);
var
pcS: PChar;
begin
pcS :='Строка символов';
pcS[5] := 'и'; {Символы в нуль-строке нумеруются,начиная с 0}
IbOutput.Caption := pcS; // Выводится “Строки символов”
end;
Программа потребует от ОС 15 + 1 = 16 байт, разместит в памяти цепочку символов и завершающий ее терминальный 0 и поместит адрес выделенного участка памяти в pcs.
Примечание
В стандартном паскале, при обращении к области памяти, на которую ссылается указатель , требуется за именем указателя ставить специальный символ .“^”. В Object Раsса1 интенсивно используется динамическая, память, и поэтому, это жесткое требование смягчено: в большинстве случаев (и при обращении к типу pchar в том числе) символ "^" ставить не следует.
И, наконец, последнее объявление acs как массива символов. В Object Pascal считается совместимым с pchar одномерный массив символов с нулевой нижней границей. В отличие от pcs память для такой переменной выделяется статически (в нашем примере компилятор выделит в сегменте данных для переменной acs 1001 байт).
Для размещения в acs нуль-терминальной цепочки символов используется процедура strcopy:
procedure' TfmExample.bbRunClick(Sender: TObject) ;
var
acS: array [0..1000] of Char;
begin
StrCopy(acS,'Строка символов') ;
acS[5] := 'и'; { Символы в нуль-строке нумеруются,начиная с 0}
lbOutput.Caption := acS; // Выводится “Строки символов”
end;
Необходимость в нуль-терминальных строках возникает только при прямом обращении к API-функциям ОС. При работе с компонентами Delphi в основном используются более удобные длинные строки, которые рассматриваются в п. 7.3.1.
И несколько слов о широких строках, 32-разрядные версии Windows используют три сорта символов: однобайтный символ ANSI, двухбайтный символ и символ Unicode. Однобайтный символ связан с одним из 256 возможных значений, которые трактуются в зависимости от установленной в Windows национальной страницы (для размещения кириллицы используется страница 1251). 256 символов вполне достаточны для отображения национального алфавита любого европейского языка. Для отображения алфавитов некоторых азиатских языков этого недостаточно. В этом случае используется двухбайтный символ, в котором младший байт обычно кодируется семибитным ASCII-кодом, а старший указывает, как должен трактоваться этот код (каким символом он будет изображаться в документе или на экране). Символ Unicode в памяти занимает одно слово, которое имеет 65536 возможных значений. Специальная международная комиссия по Unicode выработала соглашение, позволяющее с помощью этого кода представить все символы всех языков мира. Двухбайтные символы и символы Unicode объявляются стандартным типом widecnar, а составленные из них строки - типом widestring. Все Windows-программы, использующие OLE-технологию обмена строками, должны кодировать символы в соответствии с Unicode.