Указатели и их объявление

Для работы с динамическими программными объектами в Паскале предусмотрен ссылочный тип или тип указателей. В переменной ссылочного типа хранится ссылка на программный объект (адрес объекта). Указатель состоит из сегмента и смещения. По правилам Паскаля указатели на разные типы данных имеют различные типы, причем эти типы несовместимы, т.е. указатели на разные типы данных не могут ссылаться на один объект.

Чтобы связать ссылочный тип с определенным типом данных, используется символ ^, помещаемый перед именем типа. Например, имеется тип массив:

Type

A = Array[1..100] of integer;

Тип указателя на такой объект:

Type

tA = ^A;

Переменные ссылочного типа могут определяться как статические, при этом по общим правилам объявления переменных возможна запись с явным и неявным определением ссылочного типа.

Type

A = Array[1..100] of integer; {Тип массив из 100 целых чисел}

tA = ^A; {Тип указатель на тип А}

Var

B: tA; {Указатель на тип А}

C: ^A; {Указатель на тип А}

Для получения данных, соответствующих указателю, символ "^" приводится после имени указателя. Действия с элементами массива типа А могут быть описаны через действия над указателями В и С.

B^[i]:= i; {i-му элементу массива, на который указывает В, присвоить значение i}

C^[i]:= B^[i]; {i-му элементу массива, на который указывает С, присвоить значение i-го элемента массива, на который указывает В}

После выполнения этого кода i-е элементы массивов, на которые указывают В и С, будут равны.

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

Разберем простой пример, обратив еще раз внимание на синтаксис:

Var

P: ^integer; {Указатель на целое}

P^ – переменная целого типа, на которую ссылается Р, она может стоять как в левой, так и в правой части выражений:

P^:=16;

x:= x+P^;

Первый оператор присваивает целочисленной переменной, на которую ссылается Р, значение 16, второй прибавляет к значению переменной х значение 16, второй прибавляет к значению переменной х значение 16. Аналогично определяются и используются указатели на переменные любого типа, в том числе и определенного пользователем.

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

Схематически можно представить себе указатель так:

адрес

Указательная переменная Р может быть в трех состояниях.

1. Содержать адрес какой-либо переменной, память под которую уже выделена.

адрес

2. Содержать специальный пустой адрес Nil.

3. Находиться в неопределенном состоянии.

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

Схематически различия между состоянием Nil и неопределенным состоянием можно изобразить так:

Использование имени указателя в программе означает обращение к адресу ячейки памяти, на которую он указывает. Чтобы обратиться к содержимому ячейки, на которую указывает указатель, требуется после его идентификатора поставить символ ^. Эта операция еще называется разыменованием.

Для представления указателя на строку с завершающим нулем в Паскале имеется предопределенный тип PChar. В модуле System этот тип описывается следующим образом:

Type

PChar = ^char;

Паскаль поддерживает набор расширенных правил, позволяющих работать со строками с завершающим нулем, используя тип PChar.

Иногда связи между ссылкой и переменной не существует по смыслу задачи, в этом случае с указателем нельзя связать никакой объект, а ссылка должна быть пустой. Зарезервированное слово Nil обозначает константу со значением указателя, который ни на что не указывает. После присвоения

P:= Nil;

указатель не будет указывать ни на какой объект. Значение Nil совместимо с любым ссылочным типом.

Операции "=" и "<>" могут использоваться для сравнения операндов типа указатель. Два указателя равны только в том случае, если они ссылаются на один и тот же объект.

Занятие 2. Присвоение значений указателю. Оператор @ с переменной. Оператор @ с параметром процедуры, переданным по значению. Оператор @ с параметром процедуры, переданным по ссылке.

Переменной-указателю можно присвоисть значение другого указателя того же типа. В языке существует универсальный тип указателя, его имя – pointer. Используя тип pointer как промежуточный, можно присвоить значение одного указателя другому и при несовпадении их типов.

Для инициализации указателей в Паскале предусмотрены специальные процедуры и функции.

Переменной-указателю можно присвоить значение с помощью процедуры new, операции @ или функции Ptr.

Процедура new отводит блок памяти в области для динамических переменных и сохраняет адрес этого блока в указателе.

Операция @ ориентирует переменную-указатель на область памяти, содержащую уже существующую переменную. Ее можно применять к статическим переменным, динамическим переменным, процедурам и функциям.

Функция Ptr ориетирует переменную-указатель на определенный адрес в памяти. Тип результата – указатель того же типа, что и Nil, т.е. он может быть назначен любой переменной-указателю.

@ – это унарный оператор. Тип значения указателя управляется через директуву компилятора $T. Если переключателшь отключен, то результатом является нетипизированный указатель, если включен, – то тип указателя соответствует типу объекта. Если оператор @ применяется к процедуре, функции или методу, то результатом всегда будет указатель типа pointer, независимо от состояния опции $T.

Оператор @ с переменной

Использование @ с обычной переменной (не параметром процедуры) несложно.

Например, можно задать декларацию

Type

A = Array [0..99] of char; {Тип массива символов}

Var

X: Array [0..49] of integer; {Массив целых чисел}

pA: ^A; {Указатель на массив символов}

Объявлены переменные двух разных типов: массива целых из 50 элементов и ссылочного на тип А (массив из 100 символов). Чтобы указатель рА указывал на массив Х, надо присвоить ему адрес Х:

рА:= @Х; {Указатель на массив целых чисел}

Теперь pA^ ссылается на массив целых, но по своей природе он указатель на массив символов, поэтому при обращении pA^[i] мы получаем содержимое отдельных байтов массива Х в символьной форме.

Пример. Компонентам массива целых присваиваются сдвинутые на 65 значения индекса, печатается массив целых. Переменной-указателю на символьный массив присваивается адрес массива целых. Снова распечатывается массив, но по адресам значений. Вместо последовательности чисел будет напечатана последовательность ASCII символов от А до z с пробелами.

Program ReInterpretation;

{Интерпретация массива целых чисел как массива из символов}

Uses

Crt;

Type

A = Array [0..99] of char; {Тип A – массив 100 символов}

Var

X: Array [0..49] of integer; {Массив целых чисел}

pA: ^A; {Указатель на массив символов}

i: integer;

Begin

ClrScr;

for i:= 0 to 49 do

begin

X[i]:= 65+i {65 – код буквы А}

write(X[i], ' '); {Печать чисел}

End;

pA:= @X; {Указателю на А присваивается адрес массива целых чисел}

writeln;

for i:= 0 to 99 do

write(pA^[i],' '); {Печать символов}

End.

Попытка исполнить последний оператор до оператора pA:= @X привела бы к ошибке, поскольку указатель на массив был бы не опpеделен. Массив Х имеет значения 65..114, которые не выходят за пределы младшего байта двухбайтовых элементов типа word. В старших байтах этой переменной – нули. При побайтной печати массива младшие байты выводятся как буквы алфавита, а старшие – как символ #0, который процедурой write интерпретируется как пробел.

Задание. Наберите программу, откомпилируйте ее и проверьте действие.

Оператор @ с параметром процедуры, переданным по значению

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

Например, пусть Х – формальный параметр процедуры, а рХ – указатель на него. Если в процедуре выполнено назначение

рХ:= @Х;

то рХ^ указывает на значение Х, которое хранится в стеке.

Пример. Процедура Test присваивает локальному параметру р его адрес и выводит значение адреса в формате сегмент: смещение. Основная программа обращается к процедуре с фактическим параметром Р и выводит адрес фактического параметра.

Program FormalParam;

{Определение адреса формального параметра}

Uses

Crt;

Var

P: pointer;

Procedure Test(p: pointer);

Begin

p:= @P; {Определение адреса локального параметра}

writeln ('', Seg(p^), ':', Ofs(p^));

End;

Begin

ClrScr;

Test(P); {Вызов процедуры}

writeln('', Seg(p^), ':', Ofs(p^));

End.

На экран будет выведен результат исполнения программы:

В процедуре Р=1103:16378

В основной программе Р=0:0

Задание. Наберите программу, откомпилируйте ее и проверьте действие.

Оператор @ с параметром процедуры, переданным по ссылке

Применение оператора @ к формальному переменному параметру процедуры приводит к указанию на действительный параметр процедуры (указатель берется из стека).

Например, пусть

Х – формальный переменный параметр процедуры;

а – переменная, передаваемая в процедуру как действительный параметр;

рХ – ссылочная переменная.

Если процедура, как в предыдущем примере, выполняет присвоение

pХ:= @X;

то pХ – указатель на а, а pX^ – ссылка на само значение а.

Задание. Чтобы проверить выше сказанное, модифицируйте в предыдущем примере единственную строчку, передав указатель в процедуру по ссылке:

Procedure Test(Var p:pointer);

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

В процедуре Р=1119:98

В основной программе Р=1119:98

Задание. Используя одну из созданных Вами программ в теме "Процедуры и функции", модифицируйте ее, применив знания этого занятия.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: