Адресный тип данных определяется предописанным словом pointer (указатель):
Var P : pointer;
Значением переменной типа pointer является адрес поля памяти. Для размещения такой переменной отводится два слова, т.е. четыре байта. В первом слове хранится адрес сегмента, во втором - смещение.
Для указателей допустимы операция присваивания и две операции отношения ("=" и "<>"). Операция присваивания применима для двух случаев:
- присваивание значения другого указателя;
- присваивание значения предописанной константы nil, определяющей пустое значение указателя.
Операции ввода-вывода для указателей недопустимы.
Пример 1.
Var P,Q,R : pointer;
Begin
P:=nil; Q:=P;
If P<>Q then
R:=P;
Внутреннее представление константы nilимеет вид $0000:$0000. Это не нулевой адрес памяти. Значение P = nilозначает, что указатель P является неопределенным, он не адресует никакого поля памяти.
Для работы с указателями могут быть использованы следующие функции:
1) Addr(X) : pointer - адрес переменной X;
2) Seg(X) : word - сегмент адреса переменной X;
3) Ofs(X) : word - смещение адреса переменной X;
4) Ptr(Sg,Of:word) : pointer - формирование указателя, задаваемого сегментом Sg и смещением Of.
Для указателя переменной может быть использован также оператор получения адреса @, что аналогично по своему действию функции Addr: .
Пример 2.
Var P,Q,R : pointer;
m,n : integer;
a,b : real;
Sg,Of : word;
Begin
P:=Addr(m); Q:= @b;
Sg:=Seg(m); Of:=Ofs(m);
R:=Ptr(Sg,Of);
Как уже было отмечено, значение указателя не может быть выведено на печать. Тем не менее адрес, определяемый указателем P, может быть отпечатан по частям в виде значений двух целых переменных, определяющих сегмент и смещение. Эта работа может быть выполнена несколькими способами.
Пример 3.
Способ 1.
Var b : real;
Sg,Of : word;
P : pointer;
Begin
P:= @b; Sg:=Seg(b); Of:=Ofs(b);
Writeln('Сегмент ',Sg,' Смещение ',Of);
Способ 2.
Varb : real;
P : pointer;
Begin
P:= @b;
Writeln('Сегмент ',Seg(b),' Смещение ',Ofs(b));
Для примера 3 соотношение между полями P и b может быть изображено так, как это показано на рис.1.
Ссылка на поле b (чтение содержимого поля b или запись значения в поле b) может быть организована двумя способами: по имени b (прямая адресация) или по адресу, хранящемся в поле P (косвенная адресация).
В первом случае записывают a := b или b := 7 . Во втором случае, чтобы указать, что речь идет о содержимом поля памяти, адрес которого находится в поле P, пишут P^ (читается "P тильда").
Рассмотрим еще один пример.
Пример 4.
Var R : pointer;
Sg,Of : word;
Begin
Sg:=$B100; Of:=$0000;
R:=Ptr(Sg,Of);
Здесь соотношение между указателем R и полем, которое адресуется этим указателем, проиллюстрировано на рис.2.
Имя R в программе определяет лишь то, что в четырехбайтном поле R может быть размещен адрес любого поля памяти, но не определяет никаких атрибутов адресуемого поля. Поэтому в данном случае нельзя написать, например, R^ := 7 (транслятор не имеет информации о том, что может быть размещено в поле R^ - целое число, вещественное число, строка или значение переменной другого типа).
Ранее указывалось, что адрес поля памяти – это адрес его крайнего левого байта. Никакой информации о размере и структуре поля памяти этот адрес не содержит. Такая информация определяется, как правило, типом переменной, которая поставлена в соответствие с данным адресом.
В программе, иллюстрируемой рисунком 1, атрибуты поля b известны. Тем не менее здесь, как и для поля R^, нельзя написать P^ := 7, ибо на этапе трансляции невозможно определить, адрес какого поля памяти будет занесен в поле P в процессе выполнения программы. Принципиальную непредсказуемость заполнения поля P можно наблюдать на следующем фрагменте программы:
Read(Sg,Of); P:=Ptr(Sg,Of);
Для обработки полей, адресуемых указателем, можно использовать аппарат приведения типов. Тогда мы можем записать, например,