В Турбо Паскале имеется унарная операция @, которая присваивает указателю адрес переменной, являющейся операндом операции: p := @n. После выполнения этой операции указатель p будет указывать на переменную n. Следует отметить, что эта операция не требует, чтобы типы переменной и указателя были согласованы (этим Турбо Паскаль отличается от стандартного Паскаля). Однако лучше без необходимости не смешивать переменные различных типов.
Указателю в Паскале можно присвоить значение специальной предопре¬деленной константы с именем Nil (типа pointer), что означает, что указатель не имеет определенного значения и на него нельзя ссылаться.
Использование указателей в Паскале обусловлено двумя различными обстоятельствами. Во-первых, в некоторых задачах использование указателей позволяет унифицировать действия, которые без использования указателей кажутся сложными и запутанными. Сюда можно отнести обработку списков, очередей, деревьев, поддержку индексов массивов. Во-вторых, указатели необходимы при использовании динамической памяти. Зачастую обе указанные причины переплетаются.
Образование динамической переменной в Паскале осуществляется функцией new, параметром которой должен служить типизированный указатель. Функция new выделяет столько байтов, сколько требуется для динамической переменной заданного типа, а адрес начала выделенной памяти присваивает указателю. После этого динамической переменной можно присвоить какое-либо значение. Следует отметить, что единственный доступ к динамической памяти – это содержимое указателя. Достаточно изменить значение указателя – и динамическая переменная потеряна для программы навсегда. После того, как динамическая переменная перестанет быть нужна, ее можно уничтожить (а занятую ею память освободить для дальнейшего использования) функцией dispose. В качестве параметра функции dispose нужно передать указатель, ссылающийся на уничтожаемую динамическую переменную. После уничтожения динамической переменной указатель на нее теряет смысл, хотя значение адреса сохраняется. Хороший стиль программирования заключается том, что после освобождения памяти функцией dispose(p) указателю p должно быть присвоено значение Nil.
Для пояснения оперирования с указателями приведем несколько примеров. Пусть переменные определены следующим образом:
type
tp1 = array [1..10] of integer;
var
n1,n2: integer;
p1,p2: ^integer
p3: ^real;
p4: ^tp1;
p5: pointer;
После операторов
n1 := 3; n2 := 5; p1 := @n1; p2 := @n2;
указатель p1 будет содержать адрес переменной n1, а указатель p2 будет содержать адрес переменной n2. При этом будет выполняться p1^=3, p2^=5. После присваивания p2:=p1 переменная p2 будет ссылаться на n1 и окажется p2^=3. После присваивания p2^:=1 значение 1 занесется в переменную n1 и теперь окажется p1^=p2^=n1=1, n2=5.
Пусть теперь мы хотим выделить память новым динамическим переменным:
new (p1);
new (p3);
new (p4);
В свободной оперативной памяти выделится 2,6 и 20 байтов для соответственно целой и вещественной переменной и массива из 10 целых чисел, на которые будут ссылаться указатели p1, p3 и p4. Этим переменным пока не присвоено никакого значения. Для занесения значений в динамические переменные мы можем написать примерно следующее
p1^ := 7;
p3^ := 2.75;
p4^[1] := 2;
p4^[8] :=6;
и т.д. Если в дальнейшем присвоить p2:=p1, то указатели p1 и p2 будут ссылаться на одно и то же значение. Если изменить указатель p1 оператором p1:=@n1, то на динамическую переменную будет ссылаться только указатель p2. Если изменить также и его, например, p2:=@n2, то адрес динамической переменной, образованной функцией new, будет потерян навсегда и эту динамическую переменную нельзя будет ни использовать, ни закрыть, в то время как память ею будет занята. Для того, чтобы этого не происходило, надо было предварительно освободить память процедурой dispose(p2).
8th Май 2011
|
Теги:
|