Модули могут обращаться друг к другу косвенно или рекурсивно.
При косвенном использовании модулей один из них использует другой:
Пусть в головной программе используется только модуль A:
В этом случае нет необходимости указывать и модуль B:
При рекурсивном использовании модулей они взаимно обращаются друг к другу:
Если какой-нибудь из них подключить к программе:
то будет зафиксирована ошибка:
Error 68: Circular Unit Reference (A)
Взаимное использование возможно, если модули подключить из раздела реализации:
Если в модуле имеется инициирующий раздел, то его операторы выполняются до операторов программы, к которой данный модуль подключен. Если несколько модулей с инициирующими разделами:
Const x = 1; Const x = 2; x - глобальная
Implementation Implementation переменная
подключены к программе:
WriteLn('x=', x);
то они выполняются в порядке подключения, и одноименной переменной будет присвоено последнее значение. На экран будет выведено:
Последним подключен модуль B, в котором глобальная x = 2.
Изменим порядок подключения:
WriteLn('x=', x);
В этом случае на экране появится:
так как в модуле A глобальная x = 1.
Если в программе необходим доступ ко всем переменным, в том числе и одноименным, из интерфейсов всех модулей, то указываются их составные имена, похожие на имена полей записей:
WriteLn('A.x=', A.x);
WriteLn('B.x=', B.x);
На экран будет выведено:
A.x=1
B.x=2
Между объектами реального мира, которые моделируются на компьютерах, существуют разнообразные, постоянно меняющиеся связи. Эти связи, как и сами объекты, могут появляться и исчезать. Поэтому если мы хотим описать в программе группу с переменным числом объектов, связи между которыми тоже подвержены изменениям, нужны соответствующие структуры. Эти структуры должны позволять устанавливать, изменять и разрывать связи между моделируемыми объектами, а также порождать или уничтожать сами объекты.
Для этой цели в Паскале используются ссылки и динамические переменные.
До сих пор мы рассматривали только статические структуры. Этим термином обозначаются структуры данных (массивы, множества, файлы, записи), которые возникают непосредственно перед выполнением программы в соответствии со своим описанием, существуют в течение всего времени ее выполнения, и размер которых задается заранее с помощью описания их типов и не изменяется в ходе выполнения программы.
Однако при решении многих задач мы заранее, то есть на этапе написания программы, не знаем не только размера того или иного программного объекта (структуры данных), но и даже того, будет ли вообще нужен этот объект. Такого рода программные объекты или структуры данных, возникающие уже в процессе выполнения программы по мере необходимости, размер которых определяется или изменяется при ее выполнении, называются динамическими объектами или структурами.
В Паскале для работы с динамическими объектами предусматривается специальный тип данных – ссылки или указатели. Значениями этого типа данных являются ссылки на какой-либо программный объект. По этим ссылкам осуществляется непосредственный доступ к этому объекту. На машинном языке такая ссылка представляется указанием места в оперативной памяти (адреса ячейки памяти), в котором хранится значение соответствующего объекта.
В этом случае для описания действий над динамическими объектами каждому такому объекту сопоставляется в программе простая переменная ссылочного типа. В терминах этих ссылочных переменных и описываются действия над соответствующими динамическими объектами. Значения же переменных ссылочного типа, как обычно, определяются уже в процессе выполнения программы, а для достаточно удобного формирования таких значений предусматриваются специальные операторы.
Таким образом, статическая переменная описывается в программе и обозначается с помощью имени (идентификатора), которое и является адресом ячейки памяти, где хранится значение этой переменной. Динамическая же переменная не указывается в описании переменных и ее нельзя обозначить с помощью имени. К ней можно обратиться с помощью ссылки (переменной ссылочного типа), в которой хранится адрес ячейки памяти, где записано значение одноименной динамической переменной.
Ссылки (указатели) можно описать в программе двумя способами.
При первом способе в разделе определения типов Type указывается имя типа ссылки, ставятся знаки = и ^ (карат), указывается тип переменных, к которому будет относиться эта ссылка:
Type TPntint = ^Integer;
TPntchar = ^Char;
TPntReal = ^Real;
В соответствии с этим описанием TPntint является типом ссылки на динамические объекты целого типа, TPntchar – динамические объекты символьного типа, TPntReal – динамические объекты вещественного типа. Сейчас можно описать переменные, значениями которых являются ссылки – переменные ссылочного типа:
Var a, b:TPntint;
x, y: TPntchar;
s, r: TPntReal;
При втором способе переменные ссылочного типа можно вводить обычным путем, с помощью их описания в разделе Var:
Var a, b:^Integer;
x, y: ^Char;
s, r: ^Real;
Переменные a, b, x, y, s, r будут хранить адреса ячеек памяти, где находятся значения одноименных динамических переменных. В порядке исключения такие переменные не описываются в разделе Var, а производятся в программе с помощью оператора New:
……
Оператор New, во-первых, создает динамическую переменную соответствующего одноименной ссылке типа, и, во-вторых, соединяет эту динамическую переменную со ссылкой на нее.
Таким образом, оператор New играет для динамической переменной ту же роль, что и описание для статической.
В Паскале для работы с динамическими переменными введено понятие переменной с указателем – это переменная ссылочного типа, за которой следует символ ^ : x^, y^, a^. Стрелка после имени переменной ссылочного типа говорит о том, что речь идет не о значении данной переменной (адресе ячейки памяти), а о значении одноименной динамической переменной, то есть переменной, на которую указывает эта переменная ссылочного типа.
Таким образом, значение переменной с указателем тождественно значению одноименной динамической переменной. Она может быть использована во всех выражениях и операторах, где допускается использование переменных того типа, что и тип одноименной динамической переменной:
WriteLn(a^, ‘ ‘, b^); на экран будет выведено содержание ячеек памяти, адреса которых содержатся в
переменных a и b
WriteLn(a, ‘ ‘, b); ошибка! Нельзя выводить на экран адреса
ячеек памяти (Error 64)
a^ := 3;
b^ := 5;
WriteLn(a^, ‘ ‘, b^); на экран будет выведено: 3 5
a := b; переменная a содержит адрес той же ячейки, что и переменная b – они указывают на одну и ту же ячейку памяти
WriteLn(a^, ‘ ‘, b^); на экран будет выведено: 5 5
b := Nil; ссылка b никуда не указывает
WriteLn(a^, ‘ ‘, b^); ошибка! Значение b^ не определено!
Представим для наглядности полученные результаты в виде рисунков, причем в прямоугольниках укажем текущие значения соответствующих переменных, а стрелками – связи между ними:
New(a);
WriteLn(a^, ‘ ‘, b^);
Определены адреса a и b динамических переменных a^ и b^, но сами их значения пока не заданы, поэтому на экран будут выведены значения, оставшиеся в ячейках памяти с указанными адресами после решения предыдущей задачи.
a^ := 3;
b^ := 5;
WriteLn(a^, ‘ ‘, b^);
Обычное присваивание значений переменным: динамической переменной a^ присвоено значение 3, а динамической переменной b^ - значение 5. Эти же значения и выводятся на экран.
a := b;
WriteLn(a^, ‘ ‘, b^);
Указателю a присвоено новое значение – значение адреса ячейки памяти, хранившееся в указателе b. Поэтому сейчас оба указателя хранят один и тот же адрес – адрес b. Они указывают на одну и ту же динамическую переменную b^. Адрес же динамической переменной a^ утерян, поэтому ее значение становится недоступным. Значит, на экран будут выведены два одинаковых числа, являющиеся текущими значениями переменных a^ и b^: 5 и 5.
b := Nil;
WriteLn(a^, ‘ ‘, b^);
Указатель b принимает значение пустой ссылки Nil – он перестает указывать на какую-нибудь динамическую переменную b^, поэтому нельзя выводить на экран ее значение.
Таким образом, использование динамических переменных отличается следующим:
1. вместо описания самих динамических переменных в программе дается описание ссылок на них (статических переменных ссылочного типа), поставленных в соответствие одноименным динамическим переменным,
2. до своего использования каждая динамическая переменная должна быть порождена с помощью оператора New,
3. значение динамической переменной задается с помощью переменной с указателем,
4. к переменной с указателем можно применять все те операции, которые допустимы и для статической переменной того же типа.
Над значениями переменных ссылочного типа – адресами динамических переменных – в Паскале кроме операции присваивания определены две операции сравнения: = и <>.
Два значения ссылочного типа равны между собой, если они оба равны Nil, либо указывают на один и тот же динамический объект (ячейку памяти). Во всех остальных случаях имеет место неравенство.
Если в процессе выполнения программы динамический объект, созданный оператором, например New(a), становится ненужным, то его можно уничтожить оператором Dispose(a). При этом динамический объект, на который указывает ссылочная переменная a , уничтожается, занимаемое им место в памяти освобождается, а значение переменной a становится неопределенным. Оператор Dispose уничтожает только сам динамический объект, но не указатель на него:
New(a);
a^ := 3;
b^ := 5;
Dispose(a);
a := b;
b := Nil;
Основное преимущество, предоставляемое ссылками и динамическими переменными, состоит в том, что они дают возможность создавать объекты со сложной, изменяющейся в процессе работы программы структурой – динамические структуры данных – связные списки, стеки, очереди, деревья.
Динамические структуры не имеют постоянного размера, поэтому память под отдельные элементы таких структур выделяется в момент, когда они создаются в процессе выполнения программы, а не во время трансляции. Когда в элементе структуры нет необходимости, занимаемая им память освобождается.
Поскольку элементы динамических структур располагаются в памяти не по порядку и даже не в одной области, адрес элемента такой структуры не может быть вычислен из адреса начального или предыдущего элемента, как при работе с массивами. Связь между элементами динамической структуры устанавливается через указатели, содержащие адреса элементов в памяти. Такое представление данных в памяти называется связным.
Таким образом, кроме информационных полей, ради которых создается структура и которые являются видимыми для пользователя, динамические структуры содержат поля для связи с другими элементами, видимые только для программиста-разработчика. С помощью связного представления данных обеспечивается высокая изменчивость структуры.
Динамические структуры обладают следующими преимуществами:
· размер структуры ограничивается только объемом памяти компьютера,
· при изменении логической последовательности элементов структуры (добавлении или удалении элемента, изменении порядка следования элементов) требуется только коррекция указателей.
С другой стороны, такие структуры обладают рядом недостатков:
· работа с указателями требует высокой квалификации программиста,
· на указатели расходуется дополнительная память
· дополнительный расход времени на доступ к элементам связной структуры.