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