过程与函数
过程与函数
过程的声明形式如下:
1 | procedure 过程名; |
带参数的过程的声明形式如下:
1 | procedure 过程名 (形参列表); |
函数的声明形式如下:
1 | function 函数名 (形参列表) : 返回值类型; |
过程相当于没有返回值的函数。
函数返回值
Result 变量
Result
是函数中内置的变量,函数结束后,会默认返回此值
1 | function GetMyValue: integer; |
函数名作返回值
除了用 Result
变量作返回值之外,函数名也可以当作返回值:
1 | function GetMyValue: integer; |
forward 声明
过程和函数可以使用 forward
进行前置声明,把具体实现往后推:
1 | function GetMyValue: integer; forward; |
external 声明
使用 external
声明表示过程和函数是来自源文件外部。
来自外部 obj
1 |
|
来自外部 dll
1 | function ExtFunctionName1: integer; external user32; |
来自外部 dll 的指定函数名
1 | function ExtFunctionName1: integer; external user32 name 'MessageBoxW'; |
来自 dll 并延迟加载
external
加上 delayed
指示字
1 | function GetSystemMetricsForDpi(nIndex; Integer; dpi: UINT): Integer; |
参数传递方式
按值传递
1 | procedure ProcName1 (arg1, arg2: Integer; arg3: Double); |
形参列表的参数名前面没有加任何关键字,默认就是 按值传递
方式
引用传递
1 | procedure ProcName2 (var arg1: Integer; var arg2: Double); |
形参中的参数名前面加上 var
关键字,表示该参数是引用传递。形参是实参的引用,对形参的修改,会改变实参的值。
常量传递
1 | procedure ProcName3 (const arg1: Integer); |
形参中的参数名前面加上 const
关键字,表示该参数是作为常量传递。
注: 如果是指针类型,表示指针为常量,不能更改指针的值,但是指针指向的内容可以被修改
输出式传递
形参中的参数名前面加上 out
关键字,表示该参数是输出式参数。类似于 C#
的 out
参数。
1 | procedure ProcName4 (s: string; out leng: integer); |
out
方式的参数与引用传递差不多。有一点点区别:out
参数不用具备初始值,它只用来把数据输出给调用方,调用者在调用前会把实参进行清空。
out
方式的参数常用于 COM
编程。
默认参数
过程与函数的声明中运行使用默认参数:
1 | procedure ProcName5 (param1: integer = 100; param2: string = 'ABCD'); |
重载
允许多个同名的过程或者函数,但是参数个数或类型不同。这个是重载,需要加上 overload
指示字:
1 | function Min (A,B: Integer): Integer; overload; |
- 需要加上
overload
指示字 - 参数个数或者类型必须有不同
- 注意不要与默认参数的过程或函数有冲突
inline 内联
内联常常用于优化程序运行速度。在调用的时候,把过程或函数的代码直接内嵌到调用方进行展开,省去了调用的流程开支。
1 | function add(X, Y: integer): integer; inline; |
使用 inline
指示字声明为内联优化模式。
特殊参数
无类型参数
无类型参数不能传值,只能指定 const
、var
、out
方式来传递。
1 | procedure func1(var param1; typeFlag: integer); |
无类型参数在过程或者函数中处理时,需要进行显式的类型转换后才能继续使用。
可以理解为调用方传递了一个无类型的指针,然后在过程或者函数处理过程中,先进行指针指向内容的类型转换,再进行处理。
短字符串参数
声明方法:
- 一是直接将变量声明为 ShortString 类型;
- 二是利用 string 后限定长度从而定义特定长度的字符串,如 string[9]
1 | procedure do1(var S1: ShortString); |
短字符串参数实际上是字符数组参数。请参考下面的数组参数
数组参数
静态数组参数
数组传参不能直接指定数组的大小索引值等,例如:
1 | procedure do1(var a: array[0..9] of integer); // 编译不通过 |
应该用以下方式:
1 | type MyArrayType = array[0..9] of integer; |
开放数组参数
开放数组参数,就是参数类型是 array 并不指定数组的大小:
1 | procedure do2(var a: array of integer); |
动态数组参数,就是先定义动态数组的类型,再声明过程或者函数:
1 | type MyArrayType = array of integer; // 先声明动态数组 |
开放数组参数和动态数组参数很相似,但是有一些细微区别:
- 动态数组类型的参数只能接纳动态数组
- 开放数组参数,处理可以接纳动态数组传递,还可以传递静态数组
1 | var s1 : array[0..100] of integer; |
无类型开放数组参数
无类型开放数组参数,是指参数是一个数组,其数组长度不确定,数组的每一项的类型也不确定。
声明方式如下:
1 | procedure do2(const a: array of const); |
Win32下实际是 array of TVarRec
Exit 和 Halt
Exit
Exit
用于退出当前过程或函数。相当于 C 语言的 return
语句。
在函数中,赋值给 Result
变量并不会退出函数,只有调用 Exit
内置过程可以退出函数体的执行。
在函数中,Exit
允许带一个参数,用于函数的返回值,传递参数必须与函数返回值类型兼容。
1 | function function_name(aInteger: integer): string; |
Halt
Halt
用于退出当前整个程序。相当于 C 语言的 exit()
函数。
Halt
过程带一个整数类型的参数,用于告知程序退出码。
1 | procedure Halt(ExitCode:Integer = 0); |
Terminate
调用 Application.Terminate 过程也可以退出当前程序
程序退出码
ExitCode
ExitCode
是一个全局变量,它用于保存程序的退出码。默认值为 0
,程序结局运行后,表示当前程序没有发生任何错误正常退出。它定义在 System
单元里。
ExitProc
ExitProc
是一个全局变量,它定义在 System
单元里,用于保存当 exe 或者 dll 退出时,自动执行一个无参数的过程。EXE 退出或者 DLL 被卸载时,先执行 ExitProc
指向的过程,再执行 Finalization
的部分。
调用约定
32位 Windows 下函数对用有多种约定,分别有:
- (1)
register
(fastcall方式,默认) - (2)
stdcall
- (3)
cdecl
- (4)
safecall
规定被 COM 调用的函数所必须遵守的兼容规则 - (5)
pascal
1 | procedure func1(var param1; typeFlag: integer); forward; stdcall; |
调用约定 | 参数传递顺序 | 清理方 | 是否用寄存器 |
---|---|---|---|
register |
未定义 | 过程、函数自身清理 | 是 |
pascal |
从左到右 | 过程、函数自身清理 | 否 |
cdecl |
从右到左 | 调用方 | 否 |
stdcall |
从右到左 | 过程、函数自身清理 | 否 |
safecall |
从右到左 | 过程、函数自身清理 | 否 |
varargs
varargs
指示字用于声明与 C
兼容的可变参数。varargs
必须与 cdecl
联用。
1 | function printf(Format: PChar): Integer; cdecl; varargs; |
过程类型
直接定义过程类型的变量,例如:
1 | var F1: function(x: integer; y: double): integer; |
另一种方式,先声明类型别名,再定义变量:
1 | type TIntProc = procedure (var Num: Integer); |
将其他过程赋值给过程类型的变量,并通过过程类型的变量进行调用:
1 | type TIntProc = procedure (var Num: Integer); |
完全可以把变量名当作就是过程名或函数名。
注: 过程类型的变量,通过 @
获取到的是过程代码的地址,使用 @@
才能获取变量本身的地址。
匿名方法
匿名方法就是过程或函数不指定具体名称,直接给出实现。
例程引用
例程可以统称过程、函数、对象中的方法
声明例程类型举例如下:
1 | type |
匿名方法
普通的过程、函数、方法可以直接赋值给例程引用类型的变量,除外,更可以将一个匿名方法赋值给例程引用类型的变量,例如:
1 | type |
匿名方法作实参传递
匿名方法作实参传递给其他过程或函数的形参,例如:
1 | type |
变量捕获
匿名方法可以直接使用母过程/函数/方法中的变量,这种叫做变量捕获
1 | type |
- 匿名方法只能捕获其父例程中定义的局部变量或形参
- 编译器内部实际上会创建一个隐藏的框架对象(frame object)来存储捕获的变量
- 被捕获的对象的生命周期将会延迟到跟框架对象(frame object)一样
exports
exports
语句用于 DLL 导出函数。
格式:
1 | exports entryName_1, ... , entryName_n; |
导出名称与代码内部函数名称不同时:
1 | exports |
以索引导出:
1 | exports |
重载函数的导出方法:
1 | exports |