VC默认为__stdcall,
BCB默认为__cdecl,
Delphi默认为__fastcall。
由于BCB使用Delphi的VCL库,
所以也必须使用__fastcall。
关键字 | 调用规则 | 参数传递方向 | 返回 | 参数寄存器 | 堆栈的清除 |
__cdecl | C调用规则 | 从右向左 | EAX | 无 | 调用者 |
__fastcall | 寄存器 | 从左向右 | EAX | EAX、EBX、ECX | 被调用者 |
__stdcall | Win32标准 | 从右向左 | EAX | 无 | 被调用者 |
__pascal | Pascal | 从左向右 | EAX | 无 | 被调用者 |
__msfastcall | Ms寄存器 | 从右向左 | EAX/EDX | ECX、EDX | 被调用者 |
摘自《面向状态的程序设计与C++ Builder》第四章第一节 Lewolf著
1.4.1 __cdecl、_cdecl、cdecl关键字
这是在C++ Builder中特有的关键字,__cdecl、_cdecl、cdecl可以用来修饰变量或者函数,其含义是指定的变量或者函数使用C语言的调用规则和命名规则。在C++ Builder中C调用规则是缺省的设置,可以通过Project的Option菜单在Advanced Compiler标签中来改变缺省设置。
对于C命名规则的变量或函数,具有大小些敏感、编译后变量名称前下划线前导符,这与Pascal语言是不一样的。对于函数,C调用规则要求使用堆栈传递参数,参数传递的顺序为自右向左依次压入堆栈,由调用者自行清除堆栈,C调用规则允许向函数传递不定参数,但这种调用方式系统需要更多的开销来完成参数的匹配,除非特殊用途一般尽量不要使用,这种调用方式最典型的例子是控制台程序中的main函数,程序运行时可以不带参数,也可以带多个参数。
在一个独立的程序中,采用那种调用规则或者命名规则影响并不十分大,但是在多种语言混合编程,或者编写带有数据输出或函数输出的可执行模块(可执行文件和动态链接库)时,命名规则和调用规则显得尤为重要,使用不当则可能导致程序无法正常运行,甚至使系统崩溃。
__cdecl、_cdecl、cdecl的使用语法如下:
cdecl <data/function definition> ;
_cdecl <data/function definition> ;
__cdecl <data/function definition> ;
例如:
int cdecl I; //编译后标识符为“_I”
void __cdecl Fun(); //编译后函数名为“_Fun”
1.4.2 __fastcall、_fastcall关键字
__fastcall和_fastcall也是C++ Builder中特有的关键字,只能用于修饰函数,其作用是指定函数使用“寄存器”调用规则,使用语法如下:
return-type _fastcall function-name(parm-list)
return-type __fastcall function-name(parm-list)
例如:
void __fastcall GetName(int Index);
使用“寄存器”调用规则的函数,其参数传递顺序为自左向右依次传递,并且将前三个参数尽可能的使用EAX、EBX、ECX这三个寄存器传递,对于前三个参数中浮点类型、结构类型等超过四个字节的变量,和第四个及以后的参数则采用堆栈来传递,因此采用“寄存器”调用规则的函数只能传递固定数量的参数。
在C++ Builder中,所有属于VCL的成员函数,必须是__fastcall类型,编译器将“寄存器”调用规则和C调用规则、Pascal调用规则以及Win32的标准等其它调用规则是同等对待的,因此__fastcall关键字不能和__cdecl、__pascal、__stdcall等关键字联合使用,也不能和__export关键字同时使用(实际上使用__fastcall规则的函数是允许输出的,只是这种调用规则编写的dll或可执行模块只能被C++ Builder或者Delphi开发的程序载入并调用输出的函数)。
被指定为“寄存器”调用的函数会在编译时被冠以前导符@,这将使得C及C++的函数命名过于混乱,但是这种调用规则有很高的执行效率,因此在C++ Builder中几乎所有VCL的成员函数都使用这种调用规则。
但是__fastcall和_fastcall是C++ Builder之前的C++编译器所没有的调用规则,为了保证兼容性,可以使程序连接以前版本的编译器生成的库文件,C++ Builder提供了-VC命令行选项,缺省状态下,该选项是关闭的,当需要时,可以通过命令行编译并选择-VC选项。
1.4.3 __msfastcall、__msreturn关键字
__msfastcall和__msreturn也是C++ Builder中特有的关键字,使用这两个关键字可以为程序提供和Microsoft兼容的“寄存器调用”规则以及和Microsoft兼容的函数返回的规则。
被__msfastcall修饰的函数,其参数传递顺序为前两个参数如果长度小于4字节,则分别使用ECX和EDX寄存器传递,其余的参数和前两个不能使用寄存器传递的参数则按照从右向左的顺序通过堆栈来传递,堆栈由被调用函数负责清除。
__msreturn是使用Microsoft兼容的“寄存器调用”返回规则,在这种返回规则中,返回值的长度在4~8个字节时,将采用EAX/EDX寄存器返回函数值。
1.4.4 __pascal、_pascal、pascal关键字
__pascal、_pascal、pascal关键字表示一个变量或者函数遵循Pascal的调用和命名规则。
在Pascal的命名规则中,对大小写并不敏感,而且不象C调用规则,会在变量和函数明成前面加前导符。Pascal调用规则中,参数的传递是按照从左到右的顺序依次压入堆栈,并且由被调用函数自行清除堆栈。
Pascal调用规则破坏了C语言的本身的调用体系,同时也带来了更多的灵活性,被表示为__pascal调用规则的函数,编译后的目标文件中,名称会被全部转换为大写字母,在Pascal语言中,过程和函数的书写是对大小些不敏感的,但是在C++ Builder中采用Pascal调用规则的函数,在C++源文件中仍然需要遵循C语言中对大小些必须严格一致的要求,Pascal调用规则的大小写不敏感性只能体现在动态链接库、或者输出、输入函数以及数据的时侯。也正是这样的原因,对一些采用Object Pascal编写的第三方控件或者书写不是很规范的Pascal程序代码,使用C++ Builder编译的时候,会出现找不到函数原型的错误,实际上可能只是程序中函数名称的大小些不一致造成的。
1.4.5 __stdcall、_stdcall关键字
在Windows程序设计中,有一种Win32的标准调用规则,基本上所有的Windows API函数都采用了这种Win32的标准调用规则,在讲__declspec(nothrow)关键字时的例子,其中宏定义就是C++ Builder中winuser.h宏定义。Win32标准调用在C++ Builder中以__stdcall、_stdcall关键字来标识。
在__stdcall、_stdcall调用规则中,函数的参数是按照从右向左的顺序依次压入堆栈,所有的参数均采用堆栈传递,而且是被调用函数负责清除堆栈。在参数的传递顺序上,Win32标准调用规则和C调用规则一样,从右向左,但和C调用规则不同的是,Win32的标准调用要求调用者必须严格按照被调用函数的参数数量及类型进行参数传递。
关于C++ Builder中函数调用规则以及命名规则的详细内容,读者参考相关书籍,这里不做过多讲解,下表给出了C++ Builder中几种调用规则的一个简单比较,具有汇编语言基础的读者可以参考本书光盘中相关代码自行分析区别。
关键字 调用规则 参数传递方向 返回 参数寄存器 堆栈的清除
__cdecl C调用规则 从右向左 EAX 无 调用者
__fastcall 寄存器 从左向右 EAX EAX、EBX、ECX 被调用者
__stdcall Win32标准 从右向左 EAX 无 被调用者
__pascal Pascal 从左向右 EAX 无 被调用者
__msfastcall Ms寄存器 从右向左 EAX/EDX ECX、EDX 被调用者