When a procedural variable is on
the left side of an assignment statement, the compiler expects a procedural
value on the right. The assignment makes the variable on the left a pointer to
the function or procedure indicated on the right. In other contexts, however,
using a procedural variable results in a call to the referenced procedure or
function. You can even use a procedural variable to pass parameters:
var
F: function(X: Integer): Integer;
I: Integer;
function
SomeFunction(X: Integer): Integer;
...
F := SomeFunction; // assign SomeFunction to F
I := F(4); //
call function; assign result to I
In assignment statements, the type
of the variable on the left determines the interpretation of procedure or
method pointers on the right. For example,
var
F, G: function: Integer;
I: Integer;
function
SomeFunction: Integer;
...
F := SomeFunction; // assign SomeFunction to F
G := F; //
copy F to G
I := G; //
call function; assign result to I
The first statement assigns a
procedural value to F. The second statement copies that value to another
variable. The third statement makes a call to the referenced function and
assigns the result to I. Because I is an integer variable, not a procedural
one, the last assignment actually calls the function (which returns an
integer).
In some situations it is less
clear how a procedural variable should be interpreted. Consider the statement
if F =
MyFunction then ...;
In this case, the occurrence of F
results in a function call; the compiler calls the function pointed to by F,
then calls the function MyFunction, then compares the results. The rule
is that whenever a procedural variable occurs within an expression, it represents
a call to the referenced procedure or function. In a case where F references a
procedure (which doesn’t return a value), or where F references a function that
requires parameters, the statement above causes a compilation error. To compare
the procedural value of F with MyFunction, use
if @F =
@MyFunction then ...;
@F converts F into an untyped pointer
variable that contains an address, and @MyFunction returns the address of MyFunction.
To get the memory address of a
procedural variable (rather than the address stored in it), use @@. For
example, @@F returns the address of F.
The @ operator can also be
used to assign an untyped pointer value to a procedural variable. For example,
var
StrComp: function(Str1, Str2: PChar): Integer;
...
@StrComp :=
GetProcAddress(KernelHandle, 'lstrcmpi');
calls the GetProcAddress function
and points StrComp to the result.
Any procedural variable can hold
the value nil, which means that it points to nothing. But attempting to call
a nil-valued procedural variable is an error. To test whether a
procedural variable is assigned, use the standard function Assigned:
if
Assigned(OnClick) then OnClick(X);
当一个程序型变量出现在赋值语句的左边时,编译器将试图在赋值语句的右边找到一个程序型的值。这样的赋值语句使左边的变量指向右边指出的函数或过程。在其他情况中,使用程序型变量将导致对该变量引用的过程或函数的调用。甚至可以用一个程序型变量传递参数。
var
F: function(X: Integer): Integer;
I: Integer;
function
SomeFunction(X: Integer): Integer;
...
F := SomeFunction; // 赋值函数SomeFunction到程序型变量F
I := F(4); //
调用函数, 将结果赋给变量I
在赋值语句中,左边变量的类型确定了对右边程序指针或方法指针的解释。例如,
var
F, G: function: Integer;
I: Integer;
function
SomeFunction: Integer;
...
F := SomeFunction; // 将函数SomeFunction赋给程序型变量F
G := F; //
复制程序型变量F到G
I := G; //
调用函数,将结果赋给变量I
第一条语句将程序型值赋给F。第二条语句复制前面的值到另一个变量。第三条语句完成了一个对被引用的函数的调用并将结果赋给变量I。因为变量 I 使一个整数变量,不是一个程序型变量,所以最后的语句实际上是调用了函数(返回一个整数的函数)。
某些情形对程序型变量的解释方式不太清晰。考虑下面的语句,
if F =
MyFunction then ...;
在这种情况下,F的出现导致一次函数调用;编译器调用F所指向的函数,然后调用函数MyFunction,然后比较结果。此时的规则可以概括为:只要程序型变量出现在表达式中,那么它表示对被引用的过程或函数的一次调用。对上面的情况,如果F引用的是一个过程(不返回值),或者F引用的是一个需要提供参数的函数,那么上面的语句将引发编译错误。要对程序型值F和函数MyFunction进行比较,可以用如下语句
if @F =
@MyFunction then ...;
这里,@F将变量F变换为包含一个地址的无类型指针变量,@MyFunction返回函数MyFunction的地址。
要获得程序型变量的内存地址(不是该变量中存储的例程地址),可以使用@@。例如,@@F返回程序型变量F的地址。
地址运算符(@)还可以用于向一个程序型变量赋予一个无类型指针的值。例如,
var
StrComp: function(Str1, Str2: PChar): Integer;
...
@StrComp :=
GetProcAddress(KernelHandle, 'lstrcmpi');
上面代码中的赋值语句调用GetProcAddress函数并将程序型变量StrComp指向函数返回的结果。
Any procedural variable can hold the value nil, which means that it points to nothing. But attempting to call a nil-valued procedural variable is an error. To test whether a procedural variable is assigned, use the standard function Assigned:
任何程序型变量都能保存空指针值nil,即不指向任何实体。但是,试图调用一个空值的(nil-valued)程序型变量将导致出错。要测试一个程序型变量是否被赋予非空的值,可以使用标准函数Assigned。例如,
if
Assigned(OnClick) then OnClick(X);
编者注
标准函数Assigned用于测试变量是否被赋予非空的值。函数声明如下:
function
Assigned(const P): Boolean;
如果P是一个非程序型,那么当表达式P <> nil返回True时,Assigned(P)返回True;当表达式P = nil返回True时,Assigned(P)返回False。
如果P是一个程序型,那么当表达式@P <> nil返回True时,Assigned(P)返回True;当表达式@P = nil返回True时,Assigned(P)返回False。
可见,Assigned函数测试的是P或@P的值是否是空指针值nil。从另一方面说,Assigned函数不会检查非程序型的P是否指向有效的数据。例如,
var P:
Pointer;
begin
P := nil;
if Assigned(P) then ...{此语句中Assigned(P)返回False}
GetMem(P, 1024); {执行后,P指向有效的存储空间}
FreeMem(P, 1024); {执行后,P不再指向有效的存储空间,但其值仍不是空指针值nil}
if Assigned(P) then ...{此语句中Assigned(P)返回True}
end;