Глава 5. Переменные и типизированные константы
Описания переменных
Описание переменной представляет собой список идентификато-
ров, которые обозначают новые переменные и их типы.
описание ------------- ---- ---- ----
переменной ->¦список иден-+->¦ : +->¦тип+-T-----------T->¦ ; +>
¦тификаторов ¦ L---- L---- ¦ ¦ L----
L------------- ¦ ---------¦
L>¦absolute+-
L---------
Тип, задаваемый для переменных, может быть идентификатором
типа, который был ранее описан в разделе описания типов того же
самого блока, или блока, в который входит данный блок, или моду-
ля, или же этот тип может быть новым определением типа.
При указании идентификатора в списке идентификаторов описа-
ния переменной этот идентификатор имеет силу идентификатора пере-
менной в том блоке, где это описание было указано. К этой пере-
менной можно обращаться из любого места этого блока, если ее
идентификатор не переопределен в блоке, входящем в первый. Пере-
определение означает, что для новой переменной используется тот
же самый идентификатор, но это использование не оказывает влияния
на значение первоначальной переменной.
Приведем пример раздела описания переменной:
var
X,Y,Z: real;
I,J,K: integer;
Digit: 0..9;
C: Color;
Done,Error: boolean;
Operator: (plus, minus, times);
Hue1,Hue2: set of Color;
Today: Date;
Results: MeasureList;
P1,P2: Person;
Matrix: array[1..10,1..10] of Real;
Переменные, описанные вне процедуры и функции, называются
глобальными переменными и располагаются в сегменте данных. Пере-
менные, описанные в самой процедуре или функции, называются ло-
кальными переменными и располагаются в сегменте стека.
Сегмент данных
Максимальный размер сегмента данных равен 65520 байт. При
компоновке программы (что автоматически осуществляется в конце
компиляции программы) глобальные переменные всех модулей, исполь-
зуемых программой, а также собственные глобальные переменные
программы, размещаются в сегменте данных.
Если для глобальных переменных требуется более 65520 байт,
то следует распределить большие структуры в виде динамических пе-
ременных. Дальнейшее описание этой темы можно найти в разделе
"Указатели и динамические переменные" настоящей главы.
Сегмент стека
Размер сегмента стека устанавливается с помощью директивы
компилятора $M и лежит в пределах от 1024 до 65520 байт. По умол-
чанию размер стека равен 16384 байт.
При каждой активизации (вызове) процедуры или функции в стек
помещается множество локальных переменных. При завершении работы
память, занимаемая локальными переменными, освобождается. В любой
момент выполнения программы общий размер локальных переменных в
активных процедурах и функциях не должен превышать размера сег-
мента стека.
Примечание: Если вы пишете приложение для Windows, то
Windows налагает на сегменты данных и стека специальные
требования, так что рабочий максимум стека и область сег-
мента данных могут быть меньше, чем упомянутые максимальные
области сегмента данных и стека.
Директива компилятора $S используется для проверок перепол-
нения стека в программе. В состоянии {$S+}, принятом по умолча-
нию, генерируется код, осуществляющий проверку переполнения стека
в начале каждой процедуры или функции. В состоянии {$S-} такие
проверки не проводятся. Переполнение стека может вызвать аварий-
ное завершение работы системы, поэтому не следует отменять про-
верки стека, если нет абсолютной уверенности в том, что перепол-
нения не произойдет.
Абсолютные переменные
Переменные можно описать так, что они будут располагаться по
определенному адресу в памяти, и в этом случае они называются аб-
солютными переменными. Описание таких переменных должно содержать
после типа оператор absolute:
описание --------- ---------- ---- ----------
абсолютной --->¦absolute+-T->¦целое без+->¦ : +->¦целое без+-T->
переменной L--------- ¦ ¦ знака ¦ L---- ¦ знака ¦ ¦
¦ L---------- L---------- ¦
¦ -------------- ¦
L------>¦идентификатор+-------------
¦ переменной ¦
L--------------
Отметим, что список идентификаторов в описании переменной
при указании оператора absolute может содержать только один иден-
тификатор.
Первая часть оператора absolute содержит сегмент и смещение,
то есть адрес, по которому переменная должна быть размещена.
CrtMode : byte absolute $0040:$0049;
Первая константа обозначает базу сегмента, а вторая опреде-
ляет смещение внутри этого сегмента. Обе константы не должны вы-
ходить за пределы диапазона от $0000 до $FFFF (от 0 до 65535).
В программах защищенного режима DOS и в Windows первую форму
оператор absolute нужно использовать очень аккуратно, если вообще
стоит это делать. Во время выполнения прикладной программы
Windows или DOS защищенного режима она может не иметь полномочий
доступа к областям памяти вне вашей программы. Попытка доступа к
этим областям памяти может привести к сбою программы.
Вторая форма оператора absolute используется для описания
переменной, которая помещается "поверх" другой переменной, то
есть по тому же самому адресу, что и другая переменная.
var
Str: string[32];
StrLen: byte absolute Str;
Это описание указывает, что переменная StrLen должна разме-
щаться с того же адреса, что и переменная Str, а поскольку первый
байт строковой переменной содержит динамическую длину строки, то
StrLen будет содержать длину Str.
Эту вторую форму оператора absolute можно без опасения ис-
пользовать при программировании в Windows или в защищенном режиме
DOS. Память, к которой вы обращаетесь, находится в области прог-
раммы.
Ссылки на переменные
Ссылка на переменную может обозначать следующее:
- переменную;
- компонент в переменной структурного или строкового типа;
- динамическую переменную, на которую указывает переменная
типa указатель.
Синтаксис ссылки на переменную имеет вид:
--------------
ссылка на -T-->¦идентификатор+----------------------------T-->
переменную ¦ ¦ переменной ¦ ^^ ------------- ¦
¦ L-------------- ¦L--+квалификатор¦<--
¦ ---------------- ¦ L-------------
+-->¦приведение типа+------+
¦ ¦ переменной ¦ ¦
¦ L---------------- L-----
¦ ---------- ------------- ¦
L-->¦выражение+->¦квалификатор+---
L---------- L-------------
Отметим, что синтаксис ссылки на переменную допускает ис-
пользование выражения, вычисляющего значение ссылочного типа. Вы-
ражение должно следовать за квалификатором, разыменовывающим ссы-
лочное значение (или индексирующим значением указателя, если с
помощью директивы {$X+} разрешен расширенный синтаксис), что дает
фактическую ссылку на переменную.
Квалификаторы
Обращение к функции представляет собой идентификатор пере-
менной с несколькими квалификаторами или без них, которые изменя-
ют значение обращения к функции.
-------
квалификатор --T-->¦индекс+---------->
¦ L------- ^
¦ ------------- ¦
+-->¦ десигнатор +--+
¦ ¦ поля ¦ ¦
¦ L------------- ¦
¦ ---- ¦
L-->¦ ^ +------------
L----
Идентификатор массива без квалификатора является ссылкой на
весь массив, например:
Results
Идентификатор массива с указанным индексом обозначает конк-
ретный элемент массива, в данном случае структурную переменную:
Results[Current+1]
В случае, если элементом является запись, за индексом можно
указать обозначение поля. В этом случае ссылка на переменную оз-
начает конкретное поле конкретного элемента массива:
Results[Current+1].Data
Десигнатор поля в указателе-поле может сопровождаться сим-
волом указателя (^) с тем, чтобы указать различие между указате-
лем-полем и динамической переменной, на которую он указывает.
Results[Current+1].Data^
Если переменная, на которую указывается, является массивом,
то можно добавить индексы для обозначения компонентов этого мас-
сива.
Results[Current+1].Data^[J]
Массивы, строки и индексы
Конкретный элемент массива обозначается с помощью ссылки на
переменную массива, за которой указывается индекс, определяющий
данный элемент.
Конкретный символ в строковой переменной обозначается с по-
мощью ссылки на строковую переменную, за которой указывается ин-
декс, определяющий позицию символа.
---- ---------- ----
индекс -->¦ [ +------->¦выражение+----T-->¦ ] +-->
L---- ^ L---------- ¦ L----
¦ ---- ¦
L-------+ , ¦<--------
L----
Индексные выражения обозначают компоненты в соответствующей
размерности массива. Число выражений не должно превышать числа
индексных типов в описании массива. Более того, тип каждого выра-
жения должен быть совместимым по присваиванию с соответствующим
индексным типом.
В случае многомерного массива можно использовать несколько
индексов или несколько выражений в индексе. Например:
Matrix[I][J]
что тождественно записи:
Matrix[I,J]
Строковую переменную можно проиндексировать с помощью оди-
ночного индексного выражения, значение которого должно быть в ди-
апазоне 0...n, где n - указанный в описании размер строки. Это
дает доступ к каждому символу в строковом значении, если значение
символа имеет тип Char.
Первый символ строковой переменной (индекс 0) содержит дина-
мическую длину строки, то есть Length(S) тождественно Ord(S[0]).
Если атрибуту длины присваивается значение, то компилятор не про-
веряет, является ли это значение меньшим описанного размера стро-
ки. Вы можете указать индекс строки и вне ее текущей динамической
длины. В этом случае считываемые символы будут случайными, а
присваивания вне текущей длины не повлияют на действительное зна-
чение строковой переменной.
Когда с помощью директивы компилятора {$X+} разрешен расши-
ренный синтаксис, значение PChar может индексироваться одиночным
индексным выражением типа Word. Индексное выражение задает смеще-
ние, которое нужно добавить к символу перед его разыменованием
для получения ссылки на переменную типа Char.
Записи и десигнаторы полей
Конкретное поле переменной-записи обозначается с помощью
ссылки на переменную-запись, после которой указывается обозначе-
ние поля, специфицирующее это поле.
---- --------------
обозначение поля --->¦ . ¦--->¦идентификатор¦--->
L---- ¦ поля ¦
L--------------
Приведем несколько примеров десигнаторов полей:
Today.Year
Results[1].Count
Result[1].When.Month
В операторе, входящем в оператор with, обозначению поля не
должна предшествовать ссылка на переменную, содержащую запись.
Десигнаторы компонентов объекта
Формат десигнатора компонента объекта совпадает с форматом
десигнатора поля записи. То есть, он состоит из экземпляра (ссыл-
ки на переменную), за которым следует точка и идентификатор ком-
понента. Десигнатор компонента, который обозначает метод, называ-
ется десигнатором метода. К экземпляру объектного типа можно
применить оператор with. В этом случае при ссылке на компоненты
объектного типа экземпляр и точку можно опустить.
Экземпляр и точку можно опустить также в любом блоке метода.
При этом эффект будет тот же, что и при записи перед ссылкой на
компонент Self и точки.
Переменные-указатели и динамические переменные
Значением переменной-указателя является или nil (то есть
пустое значение), или адрес значения, указывающий на динамическую
переменную.
Ссылка на динамическую переменную, на которую указывает пе-
ременная-указатель, записывается в виде переменной-указателя,
после которой ставится символ указателя (^).
Динамические переменные и значения их указателей создаются с
помощью стандартных процедур New и GetMem. Вы можете использовать
операцию @ и стандартную функцию Ptr для создания значений указа-
теля, которые рассматриваются как указатели динамических перемен-
ных.
Значение nil не указывает ни на какую переменную. Если вы
попытаетесь получить доступ к динамической переменной при неопре-
деленном значении указателя или указателе, равном nil, результат
будет неопределенным.
Приведем несколько примеров ссылок (указателей) на динами-
ческие переменные:
P1^
P1.Sibling^
Results[1].Data^
Приведение типов переменных
Ссылка на переменную одного типа может быть преобразована в
ссылку на переменную другого типа с помощью приведения типов пе-
ременных.
-------------- ---- ----------- ----
приведение --->¦идентификатор+-->¦ ( +-->¦ссылка на +-->¦ ) +->
типов ¦ типа ¦ L---- ¦переменную¦ L----
L-------------- L-----------
Когда приведение типов применяется к ссылке на переменную,
ссылка на переменную рассматривается как экземпляр типа, предс-
тавленного идентификатором типа. Размер переменной (число байт,
занимаемых переменной) должен быть равен размеру типа, представ-
ленного идентификатором типа. После приведения типа переменной
можно указать один или несколько квалификаторов, если это допус-
кается указанным типом.
Примечание: Определять допустимость приведения типа
должен программист.
Приведем несколько примеров приведения типов переменных:
type
TByteRec = record
lo, hi: byte;
end;
TWordRec = record
low, high: word;
end;
TPtrRec = record
ofs, seg: word;
end;
PByte = ^Byte;
var
B: byte;
W: word;
L: longint;
P: pointer;
begin
W := $1234;
B := TByteRec(W).lo;
TByteRec(W).hi := 0;
L := $1234567;
W := TWordRec(L).lo;
B := PByte(L)^;
P := Ptr($40,$49);
W := TPtrRec(P).seg;
Inc(TPtrRec(P).Ofs,4);
end.
Обратите внимание на использование для доступа к младшим и
старшим байтам слова типа TByteRec: это соответствует встроенным
функциям Lo и Hi, только над левой частью в операции присваивание
может выполняться приведение типа. Отметим также, что для доступа
к младшим и старшим словам длинного целого, а также к смещению и
адресу сегмента указателя используются типы TWordRec и TPtrRec.
Borland Pascal также полностью поддерживает приведение типов
для процедурных типов. Например, имея следующие описания:
type
Func = function(X: Integer): Integer;
var
F: Func;
P: Pointer;
N: Integer;
вы можете построить следующие присваивания:
F := Func(P); { присвоить F значение процедурного типа в P }
Func(P) := F; { присвоить P значение процедурного типа в F }
@F := P; { присвоить F значение-указатель в P }
P := @F; { присвоить P значение-указатель в F }
N := F(N); { вызвать функцию через F }
N := Func(P)(N); { вызвать функцию через P }
Обратите в частности внимание на операцию получения адреса
@, которая применяется к переменной процедурного типа. Ее можно
использовать в левой части присваивания. Кроме того, отметьте
приведение типа на последней строке при вызове функцию через пе-
ременную-указатель.
Типизированные константы
Типизированные константы можно сравнить с инициализированны-
ми переменными - переменными, значения которых определяются на
входе в их блок. В отличие от нетипизированных констант в описа-
нии типизированной константы указывается как тип, так и значение
константы.
описание типизированной константы
¦ -------------- ---- ---- ---- ---------------
L->¦идентификатор+->¦ : +->¦тип+->¦ = +->¦типизированная+-->
L-------------- L---- L---- L---- ¦ константа ¦
L---------------
типизированная --------------------
константа ------T--->¦ константа +------->
¦ L-------------------- ^
¦ -------------------- ¦
+--->¦ адресная константа+---+
¦ L-------------------- ¦
¦ -------------------- ¦
+--->¦ константа-массив +---+
¦ L-------------------- ¦
¦ ------------------- ¦
+--->¦ константа-запись +----+
¦ L------------------- ¦
¦ -------------------- ¦
+--->¦ константа-объект +---+
¦ L-------------------- ¦
¦ -------------------- ¦
L--->¦константа-множество+----
L--------------------
Типизированные константы можно использовать точно так же,
как переменные того же самого типа, и они указываются в левой
части оператора присваивания. Отметим, что типизированные конс-
танты инициализируются только один раз - в начале выполнения
программы. Таким образом, при каждом новом входе в процедуру или
функцию локально описанные типизированные константы заново не
инициализируются.
Кроме обычных выражений-констант значение типизированной
константы может задаваться с помощью адресного выражения-констан-
ты. Адресное выражение-константа - это выражение, предусматриваю-
щее получение адреса, смещения или сегмента глобальной перемен-
ной, типизированной константы, процедуры или функции. Адресные
выражения-константы не могут ссылаться на локальные переменные
(расположенные в стеке) или динамические переменные (размещенные
в динамически распределяемой области памяти), поскольку их адреса
нельзя вычислить на этапе компиляции.
Константы простого типа
Описание типизированной константы с простым типом означает
указание значения константы:
const
Maximum : integer = 9999;
Factor : real = -0.1;
Breakchar : char = #3;
Как уже упоминалось ранее, значение типизированной константы
можно задать с помощью адресного выражение-константы, то есть вы-
ражения, в котором используются адрес, смещение или сегмент гло-
бальной переменной, типизированной константы, процедуры или функ-
ции. Например:
var
Buffer: array[0..1023] of Byte;
const
BufferOfs: Word = Ofs(Buffer);
BufferSeg: Word = Seg(Buffer);
Поскольку типизированная константа фактически представляет
собой переменную со значением константы, она не является взаимо-
заменяемой для обычных констант. Например, она не может использо-
ваться в описании других констант или типов.
const
Min : integer = 0;
Max : integer = 99;
type
Vector = array[Min..Max] of integer;
Описание Vector является недопустимым, поскольку Min и Max
являются типизированными константами.
Константы строкового типа
Описание типизированной константы строкового типа содержит
максимальную длину строки и ее начальное значение:
const
Heading : string[7] = 'Section';
NewLine : string[2] = #13#10;
TrueStr : string[5] = 'Yes';
FalseStr : string[5] = 'No';
Константы структурного типа
Описание константы структурного типа определяет значение
каждого компонента структуры. Borland Pascal поддерживает описа-
ния констант типа массив, запись, множество и указатель. Констан-
ты файлового типа и константы типа массив или запись, содержащие
компоненты файлового типа, не допускаются.
Константы типа массив
Описание константы типа массив содержит значения элементов,
заключенные в скобки и разделенные запятыми.
---- --------------- ----
константа-массив --->¦ ( +---->¦типизированная+--T->¦ ) +-->
L---- ^ ¦ константа ¦ ¦ L----
¦ L--------------- ¦
¦ ---- ¦
L------+ , ¦<---------
L----
Приведем пример константы типа массив:
type
Status = (Active,Passive,Waiting);
StatusMap = array[Status] of string[7];
const
StatStr: StatusMap = ('Active','Passive','Waiting');
В этом примере определяется константа-массив StarStr, кото-
рая может использоваться для преобразования значений типа Status
в соответствующие им строковые представления. Элементами массива
StarStr являются:
StatStr[Active] = 'Active'
StatStr[Passive] = 'Passive'
StatStr[Waiting] = 'Waiting'
Тип элемента константы-массива может быть любым, кроме фай-
лового типа. Упакованные константы строкового типа (символьные
массивы) могут быть определены и как одиночные символы, и как
строки. Определение:
const
Digits:array[0..9] of
char=('0','1','2','3','4','5','6','7','8','9');
можно представить в более удобном виде:
const
Digits: array[0..9] of char = '0123456789';
При разрешении расширенного синтаксиса (с помощью директивы
компилятора {$X+}) массивы с нулевой базой могут инициализирова-
ться строкой, которая короче, чем описанная длина массива, напри-
мер:
const
FileName = array[0..79] of Char = 'TEXT.PAS';
В таких случаях оставшиеся символы устанавливаются в NULL
(#0), и массив содержит строку с завершающим нулем.
Примечание: Подробнее о строках с завершающим нулем
рассказывается в Главе 18.
При описании константы типа "многомерный массив" константы
каждой размерности заключаются в отдельные скобки и разделяются
запятыми. Расположенные в середине константы соответствуют самым
правым размерностям. Описание:
type
Cube = array[0..1,0..1,0..1] of integer;
const
Maze: Cube = (((0,1),(2,3)),((4,5),(6,7)));
задает следующие начальные значения массива Maze:
Maze[0, 0, 0] = 0
Maze[0, 0, 1] = 1
Maze[0, 1, 0] = 2
Maze[0, 1, 1] = 3
Maze[1, 0, 0] = 4
Maze[1, 0, 1] = 5
Maze[1, 1, 0] = 6
Maze[1, 1, 1] = 7
Константы типа запись
Описание константы типа запись содержит идентификатор и зна-
чение каждого поля, заключенные в скобки и разделенные точками с
запятой.
константа-запись
¦ ---- -------------- ---- --------------- ----
L->¦ ( +--->¦идентификатор+->¦ : +->¦типизированная+-T->¦ ) +->
L---- ^ ¦ поля ¦ L---- ¦ константа ¦ ¦ L----
¦ L-------------- L--------------- ¦
¦ ---- ¦
L-------------------+ ; ¦<-------------------
L----
Приведем несколько примеров констант-записей:
type
Point = record
x,y: real;
end;
Vector = array[0..1] of Point;
Month =
(Jan,Feb,Mar,Apr,May,Jun,Jly,Aug,Sep,Oct,Nov,Dec);
Date = record
d: 1..31; m: Month; y: 1900..1999;
end;
const
Origin : Point = (x: 0.0; y: 0.0);
Line : Vector = ((x: -3.1; y: 1.5),(x: 5.8; y: 3.0));
SomeDay : Date = (d: 2; m: Dec; y: 1960);
Поля должны указываться в том же порядке, как они следуют в
описании типа запись. Если запись содержит поля файлового типа,
то для этого типа запись нельзя описать константу. Если запись
содержит вариант, то можно указывать только поля выбранного вари-
анта. Если вариант содержит поле признака, то его значение должно
быть определено.
Константы объектного типа
При описании константы объектного типа используется тот же
синтаксис, что и при описании константы типа запись. Значения для
элементов (компонентов) метода задаваться не могут. С учетом при-
водимых ранее описаний объектных типов, приведем некоторые приме-
ры констант объектного типа:
const
ZeroPoint: Point = (X: 0; Y: 0)
ScreenRect: Rect = (A: (X: 0; Y: 0); B: (X: 80; Y: 25);
CountField: NumField = (X: 5; Y: 20; Len: 4; Name: nil;
Value: 0; Min: -999; Max: 999);
Константы объектного типа, которые содержат виртуальные ме-
тоды, не требуется инициализировать с помощью вызова конструкто-
ра. Эта инициализация автоматически выполняется компилятором.
Константы множественного типа
Описание константы множественного типа может содержать нес-
колько элементов, заключенных в квадратные скобки и разделенных
запятыми. Каждый элемент такой константы представляет собой конс-
танту или отрезок типа, состоящий из двух констант, разделенных
двумя точками.
---- ----
константа-множество ->¦ [ +-T---------------------------->¦ ] +>
L---- ¦ ------------------ ^ L----
L--->¦константа-элемент+-T--
^ L------------------ ¦
¦ ---- ¦
L--------+ , ¦<---------
L----
----------
константа-элемент ---->¦константа+--T------------------------->
L---------- ¦ --- ---------- ^
L->¦..+-->¦константа+---
L--- L----------
Приведем несколько примеров констант-множеств:
type
Digits = set of 0..9;
Letters = set of 'A'..'Z';
const
EvenDigits: Digits = [0,2,4,6,8];
Vowels : Letters = ['A','E','I','O','U','Y'];
HexDigits : set of '0'..'z' =
['0'..'9','A'..'F','a'..'f'];
Константы ссылочного типа
Описание константы ссылочного типа может содержать только
значение nil (пусто). Приведем несколько примеров:
type
TDirection = (Left, Right, Up, Down);
TStringPtr = ^String;
TNodePtr = ^Node;
TNode = record
Next: NodePtr;
Symbol: StringPtr;
Value: Direction;
end;
const
S1: string[4] = 'DOWN';
S2: string[2] = 'UP';
S3: string[5] = 'RIGHT';
S4: string[4] = 'LEFT';
N1: Node = (Next: nil; Symbol: @S1; Value: Down);
N2: Node = (Next: @N1; Symbol: @S2; Value: Up);
N3: Node = (Next: @N2; Symbol: @S3; Value: Right);
N2: Node = (Next: @N3; Symbol: @S4; Value: Left);
DirectionTable: NodePtr = @N4;
Если разрешен расширенный синтаксис (указана директива ком-
пилятора {$X+}), типизированная константа типа PChar может иници-
ализироваться строковой константой, например:
const
Message: PChar = 'Программа завершена';
Prompt: PChar = 'Введите значения: ';
Digits: array[0..9] of PChar = (
'Ноль', 'Один', 'Два', 'Три', 'Четыре',
'Пять', 'Шесть', 'Семь', 'Восемь', 'Девять');
Результатом будет то, что указатель теперь указывает на об-
ласть памяти, содержащую копию строкового литерала с завершающим
нулем. Подробности вы можете найти в Главе 18 "Строки с завершаю-
щим нулем".
Константы процедурного типа
Константы процедурного типа должны определять идентификатор
процедуры или функции, совместимый по присваиванию с типом конс-
танты.
------------------
процедурная константа ------T--->¦константа-элемент+----------->
¦ L------------------ ^
¦ ------------------ ¦
+--->¦константа-элемент+---+
¦ L------------------ ¦
¦ ---- ¦
L--------->¦nil+------------
L----
Приведем следующий пример:
type
ErrorProc = procedure(ErrorCode: Integer);
procedure DefaultError(ErrorCode: Integer); far;
begin
WriteLn('Error ', ErrorCode, '.');
end;
const
ErrorHandler: ErrorProc = DefaultError;
Назад | Содержание | Вперед