Этот шаг посвящен первоначальному знакомству с указателями.
Все переменные хранят в себе какие-то значения. При описании переменной мы указываем ее тип, который определяет диапазон ее значений. Все переменные располагаются в памяти компьютера. Под размещение значений переменных отводится определенное количество байт. Однако существуют переменные, значениями которых являются адреса расположения в памяти других переменных. Такие переменные получили название указателей. Для чего нужны указатели? Ответ на этот вопрос мы дадим, когда будем изучать основы объектно-ориентированного программирования. А сейчас обратите внимание на рисунок 1, иллюстрирующий механизм использования указателей.
Рис.1. Использование указателей
Как любая другая переменная указатель должен быть описан. Однако его описание должно отличаться от обычной переменной. Поэтому в описании указателя используется символ "^". Кроме того, при описании указателя также задается тип. Но зачем же указывать тип, если указатель в качестве своего значения имеет адрес расположения в памяти другой переменной? Дело в том, что доступ к этой переменной можно получить, используя указатель. Однако возникает вопрос: а какого типа значение хранится по этому адресу? Ведь, зная тип, компьютер обрабатывает это значение соответствующим образом. Поэтому тип при описании указателей крайне необходим.
После это краткого вступления перейдем к рассмотрению синтаксических конструкций, используемых для описания указателей.
Содержательно любой ссылочный тип определяет множество значений, которые являются
указателями на значения некоторого определенного типа. Для описания
ссылочных типов используется символ "^" и идентификатор типа, например:
Type
P= ^Integer;
Это описание определяет множество указателей на целые значения. Тип, на значение которого можно конструировать указатели, может быть любым (он в данном случае называется базовым для ссылочного типа).
Имея в программе описание ссылочного типа, можно по общим правилам объявлять переменные
этого типа. Ссылочные типы в описаниях переменных можно задавать как посредством
идентификаторов, так и явно, например:
Var
P1, P2 : P; {Указатели на переменные целого типа.}
{Тип Р введен выше.}
R: ^Real; {Указатель на переменную вещественного типа.}
Описание ссылочных типов единственное исключение из общего правила,
согласно которому все идентификаторы должны быть описаны перед использованием.
В данном случае допускаются описания вида:
Type
PtrType = ^BaseType;
даже если тип BaseType еще не был описан. Однако в таком случае
BaseType должен быть описан далее в той же части описания
типов, то есть в той же самой последовательности определений типов, что и
тип PtrType:
Type
PtrType = ^BaseType;
BaseType = Record
X, Y: Real;
End; .
Реально переменные ссылочных типов содержат адреса расположения в памяти конкретных
значений базового типа. Для того, чтобы присвоить переменной ссылочного типа
некоторое значение, необходимо воспользоваться унарной операцией взятия указателя,
которое строится из знака этой операции - символа @ (амперсанд) и одного
операнда – переменной любого типа. Например, если имеется описание:
Var
i : Integer;
то применение этой операции к переменной i: @i – дает в качестве результата значение типа "указатель на целое"; поэтому оператор присваивания:
P1 : = @i;
правомочен, так как в обеих его частях стоят конструкции одного и того же типа. В результате такого присваивания P1 получит в качестве своего нового значения указатель на переменную i (адрес переменной i).
Операция взятия указателя допустима для любых переменных, в том числе для элементов массивов,
полей записи и т.д. Например, если есть описание:
Var
A : Array [1...10] of Integer;
то конструкция: @A[i] имеет смысл указателя на i-й элемент массива А и также может участвовать в присваивании, например: P1:= @A[i];.
Ссылочные типы можно образовывать от любых типов, поэтому допустимо
определение вида указатель на указатель. Так, переменная PP1
из следующего описания:
Var
PP1 : ^P;
в качестве своих возможных значений имеет множество указателей, ссылающихся на указатели, которые, в свою очередь, ссылаются на целые значения (с учетом предыдущих описаний).
Среди всех возможных указателей в языке выделяется один специальный указатель, который никуда не указывает. Это можно себе представить таким образом: в адресном пространстве оперативной памяти выделяется один адрес, в котором заведомо не может быть размещена никакая переменная. На это место в памяти и ссылается такой пустой или нулевой указатель, который обозначается служебным словом NIL. Указатель NIL считается константой, совместимой с любым ссылочным типом. Таким образом, это значение можно присваивать любому указательному типу.
Над значениями ссылочных типов допускаются две операции сравнения на равенство и
неравенство. Эти операции проверяют, ссылаются ли два указателя на одно и то
же место в памяти. Обозначаются эти операции знаками = и <>.
На следующем шаге мы посмотрим, как же осуществляется доступ к значению переменной
с помощью указателя.