Originale-mail to me for new edition

 

Procedural types in statements and expressions

 

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);

 

Topic groups

 

See also

Procedural types: Overview

 

 

译文

 

语句和表达式中的程序型类型

 

当一个程序型变量出现在赋值语句的左边时,编译器将试图在赋值语句的右边找到一个程序型的值。这样的赋值语句使左边的变量指向右边指出的函数或过程。在其他情况中,使用程序型变量将导致对该变量引用的过程或函数的调用。甚至可以用一个程序型变量传递参数。

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;              // 复制程序型变量FG

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;