- 79 -
все обращения к таким именам соответствуют обращениям к локальным переменным, а вне их — к глобальным. Локальные и глобальные переменные всегда хранятся в разных местах и даже при одинаковых именах не влияют на значения друг друга.
Все глобальные переменные хранят свои значения в так называемом сегменте данных. Его максимальный объем теоретически равен 65520 байт (почти 64К), но практически он всегда меньше на 1...2К. Таким образом, сумма размеров всех объявленных глобальных переменных должна быть меньше, чем 63К. Если этого мало, то используются ссылки и динамические переменные.
Локальные переменные существуют только при работе объявляющих процедур или функций и хранят свои значения в специально отводимой области памяти, называемой стеком. После окончания работы процедуры или функции ее локальные переменные освобождают стек. Размер стека можно менять от 1024 байт (1К) до тех же 65520 байт, используя ключ компилятора $М. По умолчанию он равен 16384 байт (16К). Это максимальный объем всех локальных переменных, работающих одновременно.
Техническая подробность: компилятор Турбо Паскаля отводит место для хранения значений глобальных переменных в сегменте данных последовательно, по мере перечисления имен. Например, объявление
| Var
| i,j : Byte; {два раза по 1 байту}
| D : Array [1..10] of Char; { десять однобайтных элементов}
| R : Real; { шесть байтов }
разместит будущие значения переменных, как показано на рис. 5.1. Этот чисто технический момент будет использован впоследствии.
Рис. 5.1
Нужно иметь в виду, что все приемы, связанные с особенностями компиляции, гарантированно работают только с Турбо Паскалем существующих на момент написания книги версий.
5.2.1. Совмещение адресов директивой absolute
Турбо Паскаль позволяет управлять при необходимости размещением значений переменных в памяти (ОЗУ). Для этой цели
- 80 -
служит директива absolute с последующим указанием адреса. Возможны два формата задания адреса, с которым в принудительном порядке будет связана переменная. Первый формат — это указание физического адреса нужной ячейки памяти:
| TYPE
| ByteArray1_10 = Array [1..10] of Byte;
| VAR
| Memory : Byte absolute $0000:$0417;
| SystemV : ByteArray1_10 absolute $B800:$0000;
| MemWord : Word absolute $0:$2;
He вдаваясь в подробности задания самого адреса $...:$..., важно отметить, как будут размещены в дальнейшем значения переменных. Все они, вместо того чтобы в порядке следования разместиться в сегменте данных, будут хранить свои значения в явно заданных (абсолютных) адресах. Причем адрес, указываемый после слова absolute, задает первый байт значения. Так, переменная Memory будет хранить и выдавать текущее значение байта системной памяти. Она же будет изменять его присваиваниями типа
Memory := 16;
{ Теперь байт по адресу $0:$417 содержит значение 16. }
Массив SystemV будет начинаться в памяти с указанного ему абсолютного адреса. В данном случае абсолютный адрес будет адресом первого элемента массива, и в этой ячейке будет хранится значение System[1]. Следующий элемент будет расположен сразу за первым в порядке возрастания адреса. Последний, десятый, элемент массива System будет соответствовать значению, хранящемуся в десятой, начиная с абсолютного адреса, ячейке памяти. И, наконец, значение длиной в слово (Word — 2 байта) MemWord будет соответствовать двум последовательным байтам памяти, первый из которых задан его абсолютным адресом в директиве absolute.
Другой формат использования слова absolute служит для совмещения описываемой переменной не с абсолютным адресом ячейки, а с адресом уже объявленной ранее переменной:
| TYPE
| RArray4Type = Array [1..4] of Real;
| Rec4Type = RECORD x1,y1,x2,y2 : Real
| END;
| VAR
| X : Word;
| Y : Word absolute X;
- 81 -
{ массив из четырех значений типа Reaclass="underline" }
dim : RArray4Type;
{ запись из четырех значений типа Reaclass="underline" }
rec : Rec4Type absolute dim;
В этом примере имена X и Y — разные идентификаторы одного и того же значения. Изменение значения X равносильно изменению значения Y, ибо после объявления Y с указанием 'absolute X', ее значение будет располагаться в тех же ячейках памяти, что и X. Но для X до этого будет отведено место обычным путем — в сегменте данных. Можно совмещать разнотипные переменные, как это сделано с dim и rec. Запись с четырьмя полями вещественного типа совмещена со значением массива из четырех таких же чисел. После объявления 'absolute dim' становятся эквивалентными обращения
dim[1] и rec.x1,
dim[2] и rec.y1,
dim[3] и rec.x2,
dim[4] и rec.y2 .
Разрешение подобных совмещений открывает большие возможности для передачи значений между различными частями программ и их использования. Директива absolute — это подобие средств организации COMMON-блоков и оператора EQUIVALENCE в языке Фортран.
При совмещении переменных сложных типов (например, dim и rec) их типы не должны конструироваться по ходу объявления, а обязаны быть введены ранее в блоке TYPE. Нарушение этого правила компилятором практически не анализируется, а последствия его могут быть самыми неприятными, вплоть до «зависания» ПЭВМ (особенно при работе с сопроцессором).
Формально размеры совмещаемых переменных не обязаны быть одинаковыми. Совмещение по имени переменной — это, по сути, совмещение их начал. Поэтому можно использовать такие приемы:
| VAR
| St : String[30];
| StLen : Byte absolute St;
Здесь значение StLen совмещено со значением текущей длины строки St, которое хранится в элементе строки St[0]. Не рекомендуется совмещать большие по длине переменные с меньшими. Если в последнем примере запись значения в StLen может попортить лишь строку St, то в случае
- 82 -
| VAR
| StLen : Byte;
| St : String absolute StLen;
Другие объявления;
изменение St изменит не только байт StLen, но и значения тех переменных, которые будут объявлены после St.
Обычно на практике, совмещаются глобальные переменные с глобальными же, но можно совмещать и локальные с глобальными. Однако невозможно будет совместить глобальные переменные с локальными.
Совмещение переменных — мощное, но очень опасное при неумелом обращении средство. Помните об этом!
5.2.2. Переменные со стартовым значением или типизированные константы
Когда программа начинает работать, места под значения переменных уже отведены, но не очищены. Это означает, что в ячейках памяти может быть что угодно (остатки предыдущей программы или ее следы). Поэтому в Паскале очень важно, чтобы каждая переменная перед использованием была бы заполнена имеющим смысл или хотя бы пустым (нулевым) значением.
Выполнить это требование можно, начиная программу со «скучной» переписи переменных
х := 0; у := 10;
ch := 'z';
Особенно неприятно задавать значения массивов и записей, так как это надо делать поэлементно.
Турбо Паскаль предлагает решение этой проблемы, позволяя объявлять переменные и тут же записывать в них стартовые значения. Единственное условие: из раздела описания VAR они должны переместиться в раздел (блок) CONST. Рассмотрим объявление сложных или типизированных констант (они же переменные со стартовым значением).
Важно запомнить, что сложные константы — это обычные переменные, которые могут изменять свое значение наравне с другими. Просто они получают значение до начала выполнения программы. Но их же можно рассматривать как константы сложных типов (массивов, записей).