Использование указателей и динамических переменных

Определение и описание

Указатель – это переменная, значением которой является адрес другой переменной. Указатель может хранить адрес переменной определенного типа, соответственно, указатели имеют тип указателя на определенный тип, который является базовым для него.

Синтаксис конструкции типа указателя:

<тип указателя>::="^"<имя указываемого типа>.

Пример:

Type Ipoint=^Integer;

Var M:Ipoint;

Этот пример не имеет практического смысла, так как к переменным типа Integer проще обращаться по имени, соответствующим образом описав их.

Основное назначение указателей – создание динамических структур данных. Такие структуры строятся из безымянных переменных типа записей. В этих записях, кроме информационных полей, присутствует одно или несколько полей, являющихся указателями на записи такого же или другого типа. Поскольку такие записи безымянные, доступ к ним по имени невозможен. Они не описываются в разделе Var, в программе описывается их тип. Они создаются и уничтожаются при работе программы по мере надобности.

Чтобы описать тип записи, содержащей поля-указатели, надо сначала описать соответствующий тип указателя. Но чтобы описать тип указателя на запись определенного типа, вначале нужно описать этот тип записи. Эта дилемма разрешена простым способом – сделано единственное отступление от принципа предварительного описания, принятого в Паскале.

Вначале описывается тип указателя на тип записи, используемый для построения динамических структур, а затем описывается тип соответствующей записи.

Пример:

Type Point=^Rec; {тип указателя на записи типа Rec}

Rec=Record {тип записи}

I:integer; {Информационное поле}

p:Point {поле-указатель}

end;

Var p,q:Point; {переменные-указатели на записи типа Rec}

{Переменные-записи не описываются}

В Паскале определена константа Nil, которая согласуется с указателями любого типа. Если указатель имеет значение Nil, он ни на что не указывает.

Указателю можно присваивать значение другого указателя того же типа, или константу Nil. Пример для описаний из предыдущего примера:

p:=q; q:=Nil;

Можно сравнивать значения указателей операциями отношения "=" и "<>". Если два указателя равны, значит, они указывают на одну и ту же безымянную переменную.

Можно обращаться к переменной, на которую указывает указатель, с помощью квалификатора "^", который добавляется после имени указателя, либо после квалификатора массива, выделяющего один указатель из массива указателей. Как следует из определения квалификатора, за квалификатором указателя может следовать квалификатор, выделяющий элемент указываемой переменной. Пример:

If p=q^.p then p^:=q^ else p^.i:=q^.i;

Рассмотрим теперь примеры выполнения операций над цепочками безымянных (динамических) переменных. Пусть в программе описаны типы Point и Rec, а также переменные-указатели p и q типа Point, как в примере из предыдущего пункта. Пусть также в процессе работы программы были созданы две динамических цепочки, каждая состоящая из двух записей типа Rec. На начало одной цепочки указывает указатель p, на начало другой – указатель q.

 
 

Рассмотрим результат выполнения следующих операций, для каждой из которых две указанные цепочки являются исходными.

1)q=p;

Значение указателя p (а это адрес первой переменной в верхней цепочке) заносится в указатель q. В результате указатель q будет указывать на ту же цепочку, что и p.

 
 

Нижняя цепочка остается несвязанной ни с одним указателем. Доступ к ней утерян, а занятая ей память не освобождена. Потерянная цепочка становится мусором в динамической памяти. Ситуаций, при которых образуется мусор, следует избегать. Как это сделать, рассмотрим далее.

2)q^=p^;

Значение переменной, на которую указывает указатель p (а это первая переменная-запись в верхней цепочке), заносится в переменную, на которую указывает указатель q. Оба поля, информационное и указательное, в первой переменной нижней цепочки становятся равными соответствующим полям первой переменной верхней цепочки.

 
 

Здесь опять образуется мусор в виде одной потерянной динамической переменной.

3)q^.i=p^.i;

Значение информационного поля переменной, на которую указывает указатель p, заносится в информационное поле переменной, на которую указывает указатель q.

 
 

Значения поля-указателя в нижней цепочке не меняется, безымянные переменные не теряются.

4)q^.p=p^.p;

 
 

Значение поля-указателя переменной, на которую указывает указатель p, заносится в поле-указатель переменной, на которую указывает указатель q.

Читателям предлагается самостоятельно определить результат выполнения следующей операции:

5)q^.p=p;