Шаг 69.
Доступ к переменной по указателю

    На этом шаге мы рассмотрим механизм доступа к значению переменной через указатель.

    Рассмотрим присваивание Р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.
Текст программы можно взять здесь.

    На следующем шаге мы познакомимся со статическим и динамическими переменными.


Предыдущий шаг Содержание Следующий шаг