ГЛАВА 4. ПЕРЕМЕННЫЕ. Объявления переменных. Объявление переменных представляет собой список идентификаторов, которые обозначают новые переменные и их типы. объявление ┌────────────┐ ┌─┐ ┌───┐ ┌─┐ переменной ───Ў│список иден-├──Ў│:├──Ў│тип├─┬────────────┬─Ў│;├──Ў │тификаторов │ └─┘ └───┘ │ │ └─┘ └────────────┘ │ ┌───────┐ │ └─Ў│дирек- ├─┘ │тива │ │absolu-│ │te │ └───────┘ Тип, задаваемый для переменных, может быть идентификатором типа, который был ранее объявлен в разделе объявления типов того же самого блока, или блока, в который входит первый, или модуля; этот тип так же может быть новым определением типа. При указании идентификатора в списке идентификаторов объявления переменной этот идентификатор - это идентификатор переменной в блоке, где встретилось это объявление. К этой переменной можно обращаться из любого места этого блока, если ее идентификатор не переопределен в блоке, входящем в первый. Переопределение означает, что для новой переменной используется тот же самый идентификатор, но это использование не оказывает влияния на значение первоначальной переменной. Приведем пример раздела объявления переменных:   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 : MesureList;   P1, P2 : Person;   Matrix : array[1 .. 10, 1 .. 10] of Real; Переменные, объявленные вне процедуры и функции, называются глобальными переменными и располагаются в сегменте данных. Переменные, объявленные в самой процедуре или функции, называются локальными переменными и располагаются в сегменте стека. Сегмент данных. Максимальный размер сегмента данных равен 65520 байт. При компановке программы (что автоматически осуществляется в конце компиляции программы) глобальные переменные всех модулей, используемых программой, а также собственные глобальные переменные программы размещаются в сегменте данных. Если для глобальных переменных требуется более 65520 байт, то следует распределить большие структуры в виде динамических переменных. Дальнейшее описание этой темы можно найти в разделе "Указатели и динамические переменные" настоящей главы. Сегмент стека Размер сегмента стека устанавливается с помощью директивы компилятора $M и лежит в пределах от 1024 до 65520 байт. По умолчанию размер стека равен 16384 байт. При каждой активизации (вызове) процедуры или функции в стек помещается набор локальных переменных. При завершении работы память, занимаемая локальными переменными, освобождается. В любой момент выполнения программы общий размер локальных переменных в активных процедурах и функциях не должен превышать размера сегмента стека. Директива компилятора $S используется для проведения проверок переполнения стека в программе. В состоянии {$S+}, принятом по умолчанию, генерируется код, осуществляющий проверку переполнения стека в начале каждой процедуры или функции. В состоянии {$S-} такие проверки не проводятся. Переполнение стека может вызвать аварийное завершение работы системы, поэтому не следует отменять проверки стека, если нет абсолютной уверенности в том, что переполнения не произойдет. Абсолютные переменные. Переменные можно описать так, что они будут располагаться по определенному адресу в памяти, и в этом случае они называются абсолютными переменными. Объявление таких переменных должно содержать директиву absolute: объявление ┌────────┐ ┌─────────┐ ┌─┐ ┌─────────┐ абсолютной ───Ў│absolute├─┬─Ў│целое без├──Ў│:├──Ў│целое без├─┬─Ў переменной └────────┘ │ │ знака │ └─┘ │ знака │ │ │ └─────────┘ └─────────┘ │ │ ┌─────────────┐ │ └──────Ў│идентификатор├────────────┘ │ переменной │ └─────────────┘ Отметим что список идентификаторов в объявлении переменной при указании директивы absolute может содержать только один идентификатор. Первая форма с директивой absolute содержит сегмент и смещение, то есть адрес, где должна быть размещена переменная. CrtMode : Byte absolute $0040 : $0049; Первая константа обозначает базовый сегмент, а вторая определяет смещение внутри этого сегмента. Обе константы не должны выходить за пределы диапазона от $0000 до $FFFF (от 0 до 65535). Вторая форма с директивой absolute используется для объявления переменной, которая помещается "поверх" другой переменной, то есть по тому же самому адресу, что и другая переменная. var Str : String[32]; StrLen : Byte absolute Str; Это объявление указывает, что переменная StrLen должна размещаться с того же адреса, что и переменная Str, а поскольку первый байт строковой переменной содержит динамическую длину строки, то StrLen будет содержать длину Str. Ссылки на переменные. Ссылка на переменную может обозначать следующее: - переменную; - элемент переменной структурного или строкового типа; - динамическую переменную, на которую указывает переменная типа указатель. Синтаксис ссылки на переменную имеет вид: ┌─────────────┐ ссылка на ─┬──Ў│идентификатор├────────────────────────────┬──Ў переменную │ │ переменной │ ° ° ┌────────────┐ │ │ └─────────────┘ │ └──┤квалификатор│ў─┘ │ ┌───────────────┐ │ └────────────┘ ├──Ў│приведение типа├────┤ │ │ переменной │ │ │ └───────────────┘ │ │ ┌─────────────┐ ┌─┐ │ └──Ў│вызов функции├─Ў│^├─┘ └─────────────┘ └─┘ Заметим, что синтаксис ссылки на переменную допускает использовать вызов функции через указатель на функцию. Полученный в результате указатель затем разименовывается с тем, чтобы обозначать динамическую переменную. Квалификаторы. Ссылка на переменную представляет собой идентификатор переменной с несколькими квалификаторами или без них, которые изменяют значение ссылки на переменную. ┌──────┐ квалификатор ──┬──Ў│индекс├──────────Ў │ └──────┘ ° │ ┌───────────┐ │ ├──Ў│обозначение├──┤ │ │ поля │ │ │ └───────────┘ │ │ ┌ ─┐ │ └──Ў│ ^ ├──────────┘ └─ ┘ Идентификатор массива без квалификатора является ссылкой на весь массив, например: Results Идентификатор массива с указанным индексом обозначает конкретный элемент массива, в данном случае структурную переменную: Results[Current + 1] В случае, если компонентой является запись или объект, за индексом можно указать обозначение поля; в этом случае ссылка на переменную означает конкретное поле внутри конкретной компоненты массива. Results[Current + 1].Data Поле указатель может сопровождаться символом указателя (^) с тем, чтобы провести различие между полем указателя и динамической переменной, на которую он указывает. Results[Current + 1].Data^ Если указываемая переменная - массив, то можно добавить индексы для обозначения элементов этого массива. Results[Current + 1].Data^[J] Массивы, строки и индексы. Конкретный элемент массива обозначается с помощью ссылки на переменную массива, за которой указывается индекс, задающий элемент. Конкретный символ в строковой переменной обозначается с помощью ссылки на строковую переменную, за которой указывается индекс, задающий позицию символа. ┌─┐ ┌─────────┐ ┌─┐ индекс ──Ў│[├─────Ў│выражение├──┬──Ў│]├──Ў └─┘ °  └─────────┘ │ └─┘ │ ┌─┐ │ └───────│,│ў─────┘ └─┘ Индексные выражения обозначают элементы в соответствующей размерности массива. Число выражений не должно превышать числа индексных типов в объявлении массива. Более того, тип каждого выражения должен быть совместим по присваиванию с соответствующим индексным типом. В случае многомерного массива одинаково можно использовать несколько индексов или несколько выражений в индексе; например, Matrix[I][J] тождественно Matrix[I, J] Строковую переменную можно проиндексировать с помощью одиночного индексного выражения, значение которого должно быть в диапазоне 0 ..N, где N - указанный в объявлении размер строки. Это дает доступ к каждому символу в строковом значении, с типом Char, даваемому этому символьному значению. Первый символ строковой переменной (индекс 0) содержит динамическую длину строки; то есть Length(S) тождественно Ord(S[0]). Если атрибуту длины присваевается значение, то компилятор не проверяет, является ли это значение меньше объявленного размера строки. Вы можете указать индекс строки и вне ее текущей динамической длины. В этом случае считываемые символы будут случайными, а присваивания вне текущей длины не повлияют на действительное значение строковой переменной. Записи и обозначения полей. Конкретное поле переменной записи обозначается с помощью ссылки на переменную записи, после которой указывается обозначение поля, задающее это поле.   ┌───┐ ┌─────────────┐ обозначение поля ───Ў│ . │───Ў│идентификатор│───Ў   └───┘ │ поля │ └─────────────┘ Приведем несколько примеров обозначения полей: Today.Year Results[1].Count Results[1].When.Month В операторе, входящем в оператор with, обозначению поля не должна предшествовать ссылка на переменную, содержащую запись. Обозначения компонент объекта. При выборе компонент объекта используется такой же формат, как и при выборе поля записи; т.е. он состоит из экземпляра, точки и идентификатора компоненты. Обозначения компоненты, которая обозначает метод, называется обозначением метода. К экземпляру объектного типа может быть применен оператор with. В этом случае при ссылках на компоненты объектного типа может быть пропущен экземпляр и точка. Экземпляр и точка так же могут быть пропущены внутри блока метода, а когда они используются, то действуют так же, как если бы при ссылке к компоненте использовались Self и точка. Переменные указателей и динамические переменные. Значением переменной указателя является или nil, или адрес значения, указывающий на динамическую переменную. Ссылка на динамическую переменную, на которую указывает переменная указатель, записывается в виде переменной указателя, после которой ставится символ указателя (^). Динамические переменные и значения их указателей создаются с помощью стандартных процедур New и GetMem. Вы можете использовать оператор @ (получение адреса) и стандартную функцию Ptr для создания значений указателя, которые рассматриваются как указатели динамических переменных. Значение nil не указывает ни на какую переменную. Результат будет неопределенным, если Вы попытаетесь получить доступ к динамической переменной при неопределенном значении указателя или равном nil. Приведем несколько примеров ссылок на динамические переменные: P1^ P1^.Sibling^ Results[1].Data^ Приведение типов переменных. Ссылка на переменную одного типа может быть преобразована в ссылку на переменную другого типа с помощью приведения типов переменных.    ┌─────────────┐ ┌───┐ ┌──────────┐  ┌───┐ приведение ───Ў│идентификатор├──Ў│ ( ├──Ў│ссылка на ├──Ў│ ) ├──Ў типов │ типа │ └───┘ │переменную│ └───┘   └─────────────┘ └──────────┘ Когда приведение типов применяется к ссылке на переменную, ссылка на переменную рассматривается как экземпляр типа, представленного идентификатором типа. Размер переменной (число байтов, занимаемых переменной) должен быть равен размеру типа, представленного идентификатором типа. После приведения типа переменной можно указать один или несколько квалификаторов, если это допускается указанным типом. Приведем несколько примеров приведения типов переменных: type ByteRec = record Lo,Hi : Byte; end; WordRec = record Low, High : Word; end; PtrRec = record Ofs, Seg : Word; end; BytePtr = ^Byte; var B: Byte; W: Word; L: LongInt; P: Pointer; begin W := $1234; B := ByteRec(W).Lo; ByteRec(W).Hi := 0; L := $01234567; W := WordRec(L).Lo; B := ByteRec(WordRec(L).Lo).Hi; B := BytePtr(L)^; P := Ptr($40,$49); W := PtrRec(P).Seg; Inc(PtrRec(P).Ofs,4); end; Отметим использование типа ByteRec для доступа к байтам младшей и старшей части слова; это соответствует встроенным функциям Lo и Hi, за исключением того, что приведение переменных можно использовать также и в левой части оператора присваивания. Также обратите внимание на использование WordRec и PtrRec типов для доступа к младшему и старшему слову части длинного целого, и к сегментной части и смещению в указателе. Turbo Pascal полностью поддерживает приведение типов переменных, включая процедурные типы. Например, для объявлений type Func = function(X : Integer) : Integer; var F: Func; P: Pointer; N: Integer; можно написать следующие присваивания: F := Func(P); { присвоить процедурное значение из Р в F } Func(P) := F; { присвоить процедурное значение из F в P } @F := P; { присвоить значение указателя из Р в F } P := @F; { присвоить значение указателя из F в P } N := F(N); { вызов функции через F } N := Func(P)(N); { вызов функции через P } Заметим, что адресный оператор @, примененный к процедурной переменной может использоваться в левой части присваивания. Заметим также, что приведение типа в последжней строке вызывает функцию через переменную указатель.