Здесь вводятся в употребление статические переменные P и Q (транслятор резервирует место в памяти, необходимое для размещения ссылки). Пустые клетки в схеме означают, что пока переменные P и Q не имеют никаких ссылок на динамические объекты.
Для порождения самого динамического объекта (создания динамических переменных) используется стандартная процедура NEW, которая называется процедурой динамического размещения.
Это процедура с параметром - ссылочной переменной, сопоставленной порождаемому динамическому объекту.
Итак, по процедуре NEW (R) выполняется резервирование участка в определенном месте памяти для последующего размещения значений динамической переменной и помещение адреса этого участка в ссылочную переменную R. При этом выделяется столько ячеек памяти, сколько требует значение динамической переменной, на которую указывает R. Количество отводимых ячеек зависит от типа динамической переменной. Динамические переменные, созданные посредством NEW, называют указанными переменными.
ЗАМЕЧАНИЕ. Резервирование места для динамической переменной идет уже в ходе выполнения программы, а не при ее трансляции, как для статических переменных. Говорят, что NEW устанавливает водораздел между статическими и динамическими переменными.
КАРТАПАМЯТИ | ||
MSX-DOS | ||
Библиотека PASCAL | ||
TURBO-система | ||
Исходный текст программы | ||
Объектный ход программы | ||
Указатель ® | ----------------- | Куча |
ПРИМЕЧАНИЕ. Здесь указана примерная схема карты памяти при работе системы Турбо-Паскаль.
При использовании в программе ссылочных переменных естественно возникает вопрос, как работать с динамическим объектом, т.е. как присваивать ему то или иное значение и распоряжаться им? Другими словами, как в программе на языке Паскаль ссылаться на динамический объект? Ведь мы знаем, что динамическим переменным, в отличие от статических, не дается имени в обычном понимании. Поэтому для работы с динамическим объектом в языке используется понятие "переменная с указателем".
Имя динамической переменной складывается из имени статической переменной типа указатель, поставленной в программе в соответствие данному динамическому объекту, и символа "^" после ссылочной переменной, свидетельствующего о том, что здесь речь идет не о ее значении, а о значении того программного объекта, на который эта ссылочная переменная указывает.
НАПРИМЕР:
Р - указатель: 5 ряд, 6 место,
Р^ - человек, сидящий в 5 ряду на 6 месте;
или
А - указатель,
А^ - имя динамической переменной, на которую указывает А.
Короче, А^ - переменная "старого" типа, которой можно присваивать конкретные значения.
Итак, пусть в программе содержатся:
VAR P: ^INTEGER; Q: ^CHAR;
NEW (P); NEW (Q).
Теперь в программе порождаются динамические переменные типов INTEGER и CHAR, которым с помощью оператора присваивания можно давать конкретные значения: Р^:= 58; Q^:= 'a'.
Переменная с указателями (она синтаксически играет роль динамической переменной) используется в любых конструкциях языка как обычная переменная, в зависимости от ее типа. Так, если R есть тоже переменная типа INTEGER, то синтаксически правильно написание следующих операторов присваивания:
R:= R + P^ + 2; P^:= P^ div 3.
В качестве ссылочной переменной может использоваться и более сложная конструкция, являющаяся частичной переменной, имеющей соответствующий ссылочный тип. Так, если в программе есть описания ссылочного типа TYPE REFREAL = ^REAL и переменной этого типа VAR A: ARRAY[1..50] OF REFREAL (в силу которого значением переменной А может быть массив элементов ссылочного типа, причем каждая из ссылок указывает на вещественное значение), то в качестве ссылочной переменной может фигурировать переменная с индексом, например, А[2]^ или А[K+5]^. Значением этих переменных с указателями будут вещественные числа. Иначе, определив
TYPE P = ARRAY[1..50] OF INTEGER; VAR B: ^P,
переменная В будет указателем на массив типа Р и в этом случае элементы массива обозначаются так - В^[2], B^[K+5].
ПРИМЕР 1. Поиск буквы во множестве букв и печать общих букв двух множеств
programm SETUK;
type MN = set of char;
var A,B: ^MN; C: MN; I,D: char;
begin
¦ new(A); A^:= ['a','c','o'];
¦ write('введите букву, которую надо найти:'); rеаdln(d);
¦ if D in A^ then writeln('да')
¦ else writeln('нет');
¦ new(B); B^:= ['g','c','o']; C:= A^ * B^;
¦ writeln('общие буквы множеств:');
¦ for I:= 'a' to 'z' do
¦ if I in C then write (I,' ')
end.
ПОЯСНЕНИЕ. В этой программе используются два динамических множества А и В, а также статическое множество С. Место, занимаемое в памяти под запись множеств A и B может быть освобождено после получения множества C.
ВЫВОДЫ (отличия динамической переменной от статической):
1. Вместо описания самих динамических переменных в программе дается описание указателей, поставленных в соответствие с ними.
2. В определенном месте программы должно быть предусмотрено порождение каждой из динамических переменных с помощью процедуры NEW.
3. Для идентификации значения динамической переменной используется переменная с указателем.
Значение одного указателя можно присваивать другому указателю того же типа.
Пусть объявлены переменные VAR Q, R: ^INTEGER и указатель R содержит адрес динамической переменной, значение которой равно 1, а Q - адрес динамической переменной, значение которой равно 2:
NEW(Q); NEW(R); Q^:= 2; R^:= 1.
Тогда оператор Q:= R перешлет в Q тот же адрес, что хранится в R, т.е. теперь Q будет "показывать" на то же значение (ту же ячейку памяти), что и R, а значение, на которое показывало Q раньше, будет навсегда утеряно. Этот процесс представлен схемой:
VAR Q,R:^INTEGER; | R | Q | |||
NEW(R); | R | * | ® | R^ | |
NEW(Q); | Q | * | ® | Q^ | |
R^:=1; | R | * | ® | 1 | R^ |
Q^:=2; | Q | * | ® | 2 | Q^ |
Q:=R | R | * | 1 | R^ или Q^ | |
Q | * | 2 | Потеряно |
Можно присваивать значения одной динамической переменной другой, но того же типа. В этом случае значения обеих динамических переменных становятся равными, но значения указателей при этом не изменяются. Например, выполнена команда присваивания Q^:= R^, в результате получим одинаковое значение у двух переменных, как это показано на схеме:
До | После | ||||||
R | * | ® | 1 | R | * | ® | 1 |
Q | * | ® | 2 | Q | * | ® | 1 |
Обмен значениями динамических переменных не изменяет значения ссылочных переменных. В этом случае, в отличие от оператора Q:=R, после выполнения которого Q и R "смотрят" на одну и ту же динамическую переменную, содержащую 1, каждая переменная Q и R указывает на свою динамическую переменную, хотя они обе содержат 1.
ЗАМЕЧАНИЕ. Следует различать пустой и неопределенный указатели. Так, при объявлении с помощью VARнекоторой переменной P(VARP: ^INTEGER), ее значение является неопределенным. Если же имеет место оператор NEW(P), то ссылочная переменная получает свое конкретное значение - адрес ячейки памяти соответствующей динамической переменной P^. Переменная P может получить значение без оператора NEWтолько в случае присваивания пустой ссылки P:=NIL или ссылки, уже ранее получившей свое значение с помощью оператора NEW.
Итак, никакие действия со ссылочными переменными нельзя производить до действия оператора NEW. В примере
VAR I,J: ^ INTEGER;
NEW (I);
оператор I:= J не законен, т.к. J еще не определена, но допустим оператор J:=I.
ПРИМЕР 2. Подсчет числа вхождений заданной буквы в первое по порядку слово максимальной длины из заданного текста, заканчивающегося точкой
program FINDLITER;
type MAS = array [1..100] of string[1];
LINK = ^MAS;
var R, REZSLOVO, TEKSLOVO: LINK;
max,i,k,j: integer;
S: string[100]; BUKWA: string[1];
begin
¦ max:= -1; i:= 0; new(TEKSLOVO); new(REZSLOVO);
¦ writeln('Введитетекст: '); readln(S);
repeat
¦ for j:= 1 to length(S) do
¦ begin
¦ ¦ BUKWA:= copy(S,j,1);
¦ ¦ if (BUKWA <> ' ') and (BUKWA <> '.')
¦ then begin i:= i+1;
¦ ¦ TEKSLOVO^[i]:= BUKWA; end
¦ ¦ else if i > max then
¦ ¦ begin max:= i; R:= REZSLOVO;
¦ ¦ REZSLOVO:= TEKSLOVO;
¦ ¦ TEKSLOVO:= R; end;
¦ ¦ i:=0;
¦ end;
¦ until BUKWA = '.';
¦ writeln('Введитебукву: '); read(BUKWA); k:= 0;
¦ for i:= 1 to max do
¦ if BUKWA = REZSLOVO^[i] then k:= k+1;
¦ writeln;
¦ write(' В слово максимальной длины: ');
¦ for j:= 1 to max do write(REZSLOVO^[j]); writeln;
¦ writeln (' буква ',BUKWA,' входит', k,' раз');
end.
Заметим также в заключение, что ссылки, которые указывают на идентичные типы, можно сравнивать друг с другом с помощью знаков "=" и "< >", при этом P = Q, если:
а) P = NIL, Q = NIL;
b) P и Q указывают на один и тот же динамический объект.
Мы рассмотрели вопрос, когда речь шла о действиях над ссылочными переменными (указателями). Теперь обратимся к действиям над динамическими переменными.
Как уже говорилось выше, динамические переменные призваны более рационально использовать память в процессе работы программы. Рациональность заключается, прежде всего, в том, чтобы убирать из памяти уже ненужные данные. Это достигается с помощью оператора DISPOSE, который имеет вид DISPOSE (R), где DISPOSE - имя процедуры стирания, а R - имя ссылочной переменной, указывающей на динамическую переменную R^, подлежащую удалению.
Итак, DISPOSE освобождает память для нового использования. Динамические переменные, не стертые с помощью DISPOSE, продолжают занимать место в "куче" после окончания работы фрагмента программы (становятся "мусором"), их надо убирать.