Delphi笔记(整理)-例程(Routine)

过程和函数统称为例程(routine)
标准过程Exit 可出现在任何过程或函数中,它结束例程的执行,并立即把程序控制返回到例程调用的地方。

一个过程声明有如下格式:
procedure procedureName(parameterList); directives;
localDeclarations;
begin
statements
end;

函数声明和过程声明类似,除了它要指定一个返回值的类似和返回值。函数声明有如下格式:
function functionName(parameterList): returnType; directives;
localDeclarations;
begin
statements
end;
函数名本身也扮演一个特殊的变量,它和内置的变量Result 一样,存储函数的返回值。

Result 和函数名并不是能完全互换的,当函数名出现在赋值语句的左边时,编译器假设它
用来跟踪(存储)返回值(就像Result);在任何其它情况下,编译器把它解释为对它的递归调用。而对
Result,它可以作为变量用在运算、类型转换、集合构造器、索引以及调用其它例程。

只要启用了扩展语法({$X+}),Result 在每个函数中被隐含声明,不要试图重新声明它。
若还没有给Result 或函数名赋值,程序就结束了,则函数的返回值没有被定义(undefined)。

在声明过程或函数时,你可以使用下面的指示字之一来指明调用约定:register、pascal、cdecl、stdcall
以及safecall。比如,
function MyFunction(X, Y: Real): Real; cdecl;
...
调用约定决定了参数被传递给例程的顺序,它们也影响从堆栈中删除参数、传递参数时寄存器的使用,
以及错误和异常处理。默认的调用约定是register。
• register 和pascal 调用从左到右传递参数,也就是说,最左边的参数最早被计算并传递,最右边的
参数最后被计算和传递;cdecl、stdcall 和safecall 调用从右到左传递参数;
• 除了cdecl 调用,过程和函数在返回之前从堆栈中移除参数,而使用cdecl,当调用返回时,调用者
从堆栈中移除参数;
• register 调用能使用多达3 个CPU 寄存器传递参数,而其它调用则全部使用堆栈传递参数;
• safecall 调用实现了异常“防火墙”,在Windows 下,它实现了进程间COM 错误通知。

默认的register 调用是最有效的,因为它通常避免了要创建堆栈结构(stack frame)(访问公布属性的方
法必须使用register);当调用来自C/C++编写的共享库中的函数时,cdecl 是有用的;通常,当调用外部
代码时,推荐使用stdcall 和safecall。在Windows 中,系统API 使用stdcall 和safecall,其它操作系统
通常使用cdecl(注意,stdcall 比cdecl 更有效)。声明双重接口的方法必须使用safecall;保留pascal 调用是为了向后兼容性。

指示字near、far 和export 用在16 位Windows 编程中,它们对32 位程序没有影响,保留它们是为了向后兼容性。

在声明过程或函数时,用forward 指示字取代例程块(包括局部变量声明和语句),比如,
function Calculate(X, Y: Integer): Real; forward;

forward 声明的目的是把过程或函数标志符的作用域提前,这允许在它被实际定义之前,其它过程和函
数可以进行调用。除了能使你更灵活地组织代码外,forward 声明对相互递归调用(mutual recursion)有
时是必须的。

在声明过程或函数时,用external 指示字取代例程块,能允许你调用和程序分开编译的例程。外部例程
可以来自目标文件或动态调入库(dynamically loadable library)。
当导入一个带有可变数目参数的C++函数时,要使用varargs 指示字。比如,
function printf(Format: PChar): Integer; cdecl; varargs;
varargs 指示字只能用于外部例程,并且只能使用cdecl 调用约定。

要调用目标文件中的例程,首先要使用$L(或$LINK)编译器指示字把目标文件链接到你的程序中。比
如,
在Windows 下: {$L BLOCK.OBJ}
在Linux 下: {$L block.o}
把BLOCK.OBJ(Windows)或block.o (Linux)链接到程序或它所在的单元。然后,声明你想调用的
函数和过程:
procedure MoveWord(var Source, Dest; Count: Integer); external;
procedure FillWord(var Dest; Data: Integer; Count: Integer); external;
现在,你就能调用来自BLOCK.OBJ 或block.o 的MoveWord 和FillWord 例程了。
像上面的声明,经常用来访问由汇编语言编写的外部例程,你也可以直接在Object Pascal 源代码中放置
汇编语言写的例程。

要从一个动态调入库(.so 或.DLL)导入例程,把如下格式的指示字
external stringConstant;
放在一个正常的过程头或函数头的尾部。这里,stringConstant 是用单引号括起来的库文件的名称。比如,
在Windwos 下
function SomeFunction(S: string): string; external ’strlib.dll’;
从strlib.dll 导入一个叫做SomeFunction 的函数。
在Linux 下,
function SomeFunction(S: string): string; external ’strlib.so’;
从strlib.so 导入一个叫做SomeFunction 的函数。
在导入例程时,它的名称可以和库中的名称不同。如果你这样做,在external 指示字中指定它的原始名
称。
external stringConstant1 name stringConstant2;
这里,第一个stringConstant 给出了库文件的名称,第二个stringConstant 是例程的原始名称。
在Windows 下:比如,下面的声明从user32.dll(Windows API 的一部分)导入一个函数。
function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer;
stdcall; external ’user32.dll’ name ’MessageBoxA’;
函数的原始名称是MessageBoxA,但导入后的名称是MessageBox。
你可以使用一个数字代替名称,来指定你要导入的例程:
external stringConstant index integerConstant;
这里,integerConstant 是输出表(export table)中例程的索引。
在Linux 下:比如,下面的声明从libc.so.6 导入一个标准系统函数。
function OpenFile(const PathName: PChar; Flags: Integer): Integer; cdecl;
external ’libc.so.6’ name ’open’;
函数的原始名称是open,但导入后的名称是OpenFile。
在你的导入声明中,要保证例程的名称没有拼写错误,并且大小写一致。但在以后调用这些例程时,它
们是不区分大小写的。

重载例程必须使用overload 指示字,并且它们有不同的参数列表。

当重载例程被声明为forward、或在单元的接口部分声明时,在它的定义声明部分必须重新列出它的参数。

参数以下面几种方式进行分类:
• 每个参数分为value(数值参数)、variable(变量参数)、constant(常量参数)或out(out 参数),
默认是数值参数。关键字var、const 以及out 分别表示变量参数、常量参数和out 参数。
• 数值参数总是有类型的,而常量参数、变量参数和out 参数既可以是有类型的,也可以是无类型的。
• 数组参数有特殊规则。
文件类型以及包含文件的结构类型(的实例)只能作为变量参数传递。

数值参数通过数值传递,而变量参数通过引用传递。

如果例程声明了一个var 参数,你必须给它传递一个能被赋值的表达式,也就是一个变量、类型化常量
(typed constant,在{$J+}状态下)、dereferenced 指针、字段或者索引变量(indexed variable)

当使用out 参数时,传给例程的引用参数的初始值被忽略。out 参数只是为了输出,也就是说,它告诉
函数或过程在哪里存储输出,但不提供任何输入。
out 参数经常用在分布式对象模型中,比如COM 和CORBA。而且,当向函数或过程传递未初始化的变
量时,你应当使用out 参数。

特殊标志符OpenString 能用于声明可变长度的短字符串参数:
procedure Check(S: OpenString);
当编译器指示字{$H-}和{$P+}都起作用时,在声明参数时关键字string 等同于OpenString。
短字符串、OpenString、$H 和$P 是为了向后兼容性。在新代码中,使用长字符串来避免这种情况

开放数组参数遵循下列规则:
• 元素的下标总是从0 开始,第一个是0,第二个是1,依此类推。标准函数Low 和High 返回0 和
Length-1。SizeOf 函数返回传给例程的实际数组的大小;
• 它们只能通过元素进行访问,不允许给整个开放数组赋值;
• 它们只能被当作开放数组参数或无类型var 参数传给其它过程和函数,它们不能传给SetLength 函
数;
• 你可以传递一个变量而不是数组,变量的类型就是开放数组的基础类型,它被当作一个长度为1 的
数组。

Variant 开放数组参数允许你向一个过程或函数传递由不同类型的元素构成的数组。要定义这样一个例
程,指定array of const 作为参数的类型,这样
procedure DoSomething(A: array of const);
声明了一个叫做DoSomething 的过程,它能接收不同类型的数组。
array of const 结构等同于array of TVarRec。TVarRec 在System 单元定义,表示一个拥有变体部分的记
录,它能存储整数、布尔、字符、实数、字符串、指针、类、类引用、接口和变体类型的值。TVarRec
记录的VType 字段指示数组中每个元素的类型。一些类型以指针而不是以数值形式进行传递,特别是,
长字符串以指针类型传递,必须被转换为string。

有默认值的参数必须出现在参数列表的最后

在过程类型中指定的默认值会覆盖实际例程中指定的默认值。所以,给出下面的声明
type TResizer = function(X: Real; Y: Real = 1.0): Real;
function Resizer(X: Real; Y: Real = 2.0): Real;
var
F: TResizer;
N: Real;
语句
F := Resizer;
F(N);
导致(N, 1.0)传给Resizer。

默认参数局限于能被常量表达式所表示的值,所以,动态数组、过程、类、类引用或者接口类型的参数
除了nil 外不能给它们指定默认值,而记录、变体、文件、静态数组和对象类型则根本不能指定默认值。

若在重载例程中使用默认参数,要避免引起歧义。

开放数组构造器和集合构造器类似,是由逗号隔开的表达式序列,并且被一对中括号包围。

开放数组构造器只能当作数值参数或常量参数传递。构造器中的表达式必须和数组参数的基础类型是赋
值兼容的。对于Variant 开放数组参数,表达式可以是不同的类型。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值