function Equal(var source,dest; size: word): boolean;
type
Bytes = array[0..MaxInt] of byte;
var
N: integer;
begin
N := 0;
while (N<size) and (Bytes(dest)[N] <> Bytes(source)[N]
do Inc(N);
Equal := N = size;
end;
Эта функция может использоваться для сравнения любых двух переменных любого размера. Например, с помощью описаний:
type
Vector = array[1..10] of integer;
Point = record
x,y: integer;
end;
var
Vec1, Vec2: Vector;
N: integer;
P: Point;
и вызовов функций:
Equal(Vec1,Vec2,SizeOf(Vector))
Equal(Vec1,Vec2,SizeOf(integer)*N)
Equal(Vec[1],Vec1[6],SizeOf(integer)*5)
Equal(Vec1[1],P,4)
сравнивается Vес1 с Vес2, сравниваются первые N элементов Vес1 с первыми N элементами Vес2, сравниваются первые 5 элементов Vес1 с последними пятью элементами Vес2 и сравниваются Vес1[1] с Р.х и Vес2[2] с P.Y.
Процедурные типы
Являясь расширением стандартного Паскаля, Турбо Паскаль позволяет интерпретировать процедуры и функции, как объекты, которые можно присваивать переменным и передавать в качестве параметров. Таким образом, допускается использование процедурных типов.
Примечание: Процедурные типы определяются также в Главе 3 ("Типы").
Процедурные переменные
После определения процедурного типа появляется возможность описывать переменные этого типа. Такие переменные нызывают процедурными переменными. Например, с учетом описаний типа из предыдущего примера, можно объявить следующие переменные:
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 байта (два слова), в которых содержится адрес памяти. В первом слове хранится смещение, во втором - сегмент.
Параметры процедурного типа
Поскольку процедурные типы допускается использовать в любом контексте, то можно описывать процедуры или функции, которые воспринимают процедуры и функции в качестве параметров. В следующем примере показывается использование параметров процедурного типа для вывода трех таблиц различных арифметических функций:
program Tables;
type
Func = function(X,Y: integer): integer;
function Add(X,Y: integer): integer; far;
begin
Add := X + Y;
end;
function Multiply(X,Y: integer): integer; far;
begin
Multiply := X*Y;
end;
function Funny(X,Y: integer): integer; far;
begin
Funny := (X+Y) * (X-Y);
end;
procedure PrintTable(W,H: integer; Operation: Func);
var
X,Y : integer;
begin
for Y := 1 to H do
begin
for X := 1 to W do Write(Operation(X,Y):5);
Writeln;
end;
Writeln;
end;
begin
PrintTable(10,10,Add);
PrintTable(10,10,Multiply);
PrintTable(10,10,Funny);
end.
При работе программа Table выводит три таблицы. Вторая из них выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
Параметры процедурного типа особенно полезны в том случае, когда над множеством процедур или функций нужно выполнить какие-то общие действия. В данном случае процедуры PrintTable представляет собой общее действие, выполняемое над функциями Add, Multiply и Funny.
Если процедура или функция должны передаваться в качестве параметра, они должны удовлетворять тем же правилам совместимости типа, что и при присваивании. То есть, такие процедуры или функции должны компилироваться с директивой far, они не могут быть встроенными функциями, не могут быть вложенными и не могут описываться с атрибутами inline или interrupt.