Процедурные переменные
После определения процедурного типа появляется возможность описывать переменные этого типа. Такие переменные называют проце- дурными переменными. Например, с учетом описаний типа из предыду- щего примера, можно объявить следующие переменные:
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 байта (два слова), в которых содержится адрес памяти. В первом слове хранится смещение, во втором - сегмент.