Процедурные типы в выражениях
В общем случае использование процедурной переменной в опера- торе или выражении означает вызов процедуры или функции, храня- щейся в этой переменной. Однако, имеется исключение. Когда компи- лятор видит, что процедурная переменная находится в левой части оператора присваивания, он знает, что правая часть должна предс- тавлять собой процедурное значение. Рассмотрим в качестве примера следующую программу:
type IntFunc = function: Integer; var F: IntFunc; N: Integer;
function ReadInt: Integer; far; var I: Integer; begin Read(I); ReadInt := I; end; begin F := ReadInt; { присваивание процедурного значения } N := ReadInt; { присваивание результата функции } end.
Первый оператор основной программы присваивает процедурное значение (адрес процедуры) ReadInt процедурной переменной F, вто- рой оператор вызывает ReadInt и присваивает N возвращаемое значе- ние. Различие между получением процедурного значения или вызовом функции осуществляется по типу переменной, которой присваивается значение (F или N).
К сожалению, есть ситуации, когда компилятор не может опре- делить из контекста желаемое действие. Например, в следующем опе- раторе для компилятора не очевидно, что нужно сделать: сравнить процедурное значение в F с процедурным значением ReadInt, чтобы определить, что F указывает в данный момент на ReadInt, или выз- вать F и ReadInt, а затем сравнить возвращаемые значения:
if F = ReadInt then WriteLn('Equal');
Однако, стандартный синтаксис Паскаля определяет, что вхож- дение в выражение идентификатора функции означает вызов этой функции, поэтому в результате предыдущего оператора будет выпол- нен вызов F и ReadInt, а затем будут сравниваться возвращаемые значения. Чтобы сравнить процедурное значение в F с процедурным значением в ReadInt, нужно использовать следующую конструкцию:
if @F = @ReadInt then WriteLn('Equal');
При применении к процедурной переменной, идентификатору про- цедуры или функции операции получения адреса @, эта операция пре- дотвращает вызов компилятором процедуры и в то же время преобра- зует аргумент в указатель. Таким образом, @F преобразует F в не- типизованный указатель-переменную, которая содержит адрес ReadInt. Для определения того, что F ссылается на ReadInt можно сравнить два значения-указателя.
Операция @ часто используется при присваивании процедурной переменной нетипизированного значения-указателя. Например, опре- деленная в Windows (в модуле WinProcs) функция GetProcAddress возвращает адрес экспортируемой функции в DLL как нетипизирован- ной значение-указатель. С помощью операции @ вызов GetProcAddress можно присвоить процедурной переменной:
type TStrComp = function(Str1, Str2: PChar): Integer; var StrComp: TStrComp: . . . begin . . . @StrComp := GetProcAddress(KernelHandle, 'Lstrcmpi'); . . . end.
Чтобы получить адрес в памяти процедурной переменной, а не адрес, в ней записанный, используйте двойную операцию @ (@@). Например, @P означает преобразование P в нетипизированный указа- тель-переменную, в @@P означает возвращение физического адреса переменной P.