На этом шаге мы рассмотрим механизм доступа к значению переменной через указатель.
Рассмотрим присваивание Р1:= @i;. Эта схема показывает, что для доступа к переменной i имеются две возможности:
Первый путь достаточно очевиден; например, оператор i:= i+2; считывает текущее значение i и увеличивает его на 2. Для реализации второго, косвенного доступа к переменной через указатель (ссылку) на нее используется конструкция, называемая разыменованием. Общее правило таково: для того, чтобы по указателю на переменную получить доступ к самой этой переменной, необходимо после переменной-указателя поставить знак ^. Так, запись Р1^ означает: переменная, на которую ссылается Р1. Такая конструкция может находиться в любом контексте, в котором допустимо вхождение самой указуемой переменной. Поэтому операторы: i:= i+2 и P1^ := P1^+2 эквивалентны. Разыменование по определению имеет тип, совпадающий с базовым типом переменной-указателя; в частности, Р1^ считается переменной целого типа.
В случае указателя на указатель возможно многократное разыменование. Например, для переменной РР1, описанной на предыдущем шаге как имеющей тип указатель на указатель на целое, допустима конструкция: РР1^^, которая имеет статус целой переменной.
Разыменование считается некорректным, если ссылочная переменная имеет значение
NIL. В этом случае не существует переменной, на которую ссылается указатель,
поэтому операторы вида:
Р1 := Nil;
P1^ := 2;
хотя и не приводят к аварийному прекращению выполнения, но являются недопустимыми, и могут служить источником ошибок.
Приведем текст программы, иллюстрирующей разименование указателей. Надеемся, что текст программы и комментарии к ней помогут вам понять ее работу.
Program Problem69_1; Type PP=^Integer; Var n,m:Integer; p,p1:^Integer; pp1:^PP; {Вариант ^^Integer не проходит!} Begin WriteLn('Задайте два целых числа:'); ReadLn(n,m); p:=@n; {В p поместили адрес переменной n} WriteLn('Увеличили значение n на 2, используя разименование указателя'); p^:=p^+2; WriteLn('Теперь значение n =',n); p1:=@m; {В p1 поместили адрес переменной m} pp1:=@p1; {В pp1 поместили адрес переменной p1, которая, в свою очередь,} {содержит адрес расположения в памяти значения переменной m} WriteLn('Уменьшили значение m на 3, используя двойное разименование указателя'); pp1^^:=pp1^^-3; WriteLn('Теперь значение m =',m); WriteLn('А через двойное разименование указателя получаем: ',pp1^^); p1:=@n; {Изменили p1, поместив туда адрес переменной n} {Обратите внимание, мы не изменяли значение pp1!} WriteLn('Поместили теперь в p1 адрес переменной n вместо m.'); WriteLn('Интересно, что получим, дважды разименовав pp1.'); WriteLn('Правильно! Мы получим значение ',pp1^^); End.
На следующем шаге мы познакомимся со статическим и динамическими переменными.