Руководство по языку B.Pascal 7

Процедурные переменные


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

var P: SwapProc; F: MathFunc;

Как и целая переменная, которой можно присвоить значение це- лого типа, процедурной переменной можно присвоить значение проце- дурного типа. Таким значением может быть, конечно, другая проце- дурная переменная, но оно может также представлять собой иденти- фикатор процедуры или функции. В таком контексте описания проце- дуры или функции можно рассматривать, как описание особого рода константы, значением которой является процедура или функция. Нап- ример, пусть мы имеем следующие описания процедуры и функции:

procedure Swap(var A,B: integer); var Temp: integer; begin Temp := A; A := B; B := Temp; end.

function Tan(Angle: real): real;

begin Tan := Sin(Angle) / Cos(Angle); end.

Описанным ранее переменным P и F теперь можно присвоить зна- чения:

P := Swap; F := Tan;

После такого присваивания обращение P(i,j) эквивалентно Swap (i,j) и F(X) эквивалентно Tan(X).

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

Кроме того, для обеспечения совместимости по присваиванию процедура и функция, если ее нужно присвоить процедурной перемен- ной, должна удовлетворять следующим требованиям:

- Это не должна быть стандартная процедура или функция. - Такая процедура или функция не может быть вложенной. - Такая процедура не должна быть процедурой типа inline. - Она не должна быть процедурой прерывания (interrupt).

Стандартными процедурами и функциями считаются процедуры и функции, описанные в модуле System, такие, как Writeln, Readln, Chr, Ord. Чтобы получить возможность использовать стандартную процедуру или функцию с процедурной переменной, вы должны напи- сать для нее специальную "оболочку". Например, пусть мы имеем процедурный тип:


type IntProc = procedure(N: integer);

Следующая процедура для записи целого числа будет совмести- мой по присваиванию:

procedure WriteInt(Number: Integer); far; begin Write(Number); end.

Вложенные процедуры и функции с процедурными переменными ис- пользовать нельзя. Процедура или функция считается вложенной, когда она описывается внутри другой процедуры или функции. В сле- дующем примере процедура Inner вложена в процедуру Outer и поэто- му ее нельзя присваивать процедурной переменной:

program Nested; procedure Outer; procedure Inner; begin Writeln('Процедура Inner является вложенной'); end; begin Inner; end; begin Outer; end.

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

type GotoProc = procedure(X,Y: integer); ProcList = array[1..10] of GotoProc; WindowPtr = ^WindowRec; Window = record Next: WindowPtr; Header: string[31]; Top,Left,Bottom,Right: integer; SetCursor: GotoProc; end; var P: ProcList; W: WindowPtr;

С учетом этих описаний допустимы следующие вызовы процедур:

P[3](1,1); W.SetCursor(10,10);

Когда процедурной переменной присваивается значение процеду- ры, то на физическом уровне происходит следующее: адрес процедуры сохраняется в переменной. Фактически, процедурная переменная весьма напоминает переменную-указатель, только вместо ссылки на данные она указывает на процедуру или функцию. Как и указатель, процедурная переменная занимает 4 байта (два слова), в которых содержится адрес памяти. В первом слове хранится смещение, во втором - сегмент.


Содержание раздела