2007-6-25 13:43
__cdecl __fastcall与 __stdcall,三者都是调用约定(Calling convention),它决定以下内容:
(1)由调用者还是被调用者把参数弹出栈,
(2)以及产生函数修饰名的方法,C和C++不一样
PS:至于参数的入栈顺序,C语言是从右到左,pascal是从左到右,与调用约定没有关系。
出栈参数清理
1、__stdcall:被调用的函数在返回前清理传送参数的内存栈,
VB一般使用的是stdcall调用约定;Windows的API中,一般使用的是stdcall约定
建议在不同语言间的调用中(如DLL)最好采用stdcall调用约定,因为它在语言间兼容性支持最好;
在Windef.h中有如下的定义:
#define CALLBACK __stdcall
#define WINAPI __stdcall
2、__cdecl是C和C++程序的缺省调用方式。由调用者清理传送参数的内存栈。如果函数被调用多次,那么产生的可执行文件大小就会比__stdcall方式产生的大。但是对于可变参数的函数(参数有默认值),就只能使用__cdecl的调用方式,因为被调用者不知道传入了多少的参数。 main(或WinMain)函数的调用约定必须是__cdecl,不允许更改;
3、__fastcall调用约定:和__stdcall的区别就是用ECX和EDX传送前两个双字(DWORD)或更小的参数
调用约定可以通过工程设置:Setting...C/C++ Code Generation项进行选择,缺省状态为__cdecl。
名字修饰约定
1、C编译时函数名修饰约定规则:
__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,
格式为_functionname@number,
例如 :function(int a, int b),其修饰名为:_function@8
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,
格式为_functionname。
__fastcall调用约定在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,
格式为@functionname@number。
2、C++编译时函数名修饰约定规则:
1)、以"?"标识函数名的开始,后跟函数名;
2)、函数名后面"@@YG"表示__stdcall方式;"@@YA"表示__cdecl方式";"@@YI"表示__fastcall方式;
3)、参数表以代号表示:
X--void ,D--char,E--unsigned char,F--short,H--int,I--unsigned int,
J--long,K--unsigned long,M--float,N--double,_N--bool,
PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
整个格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ"
例如
int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”
void Test2()-----“?Test2@@YGXXZ”//第1个X表示输出参数是void,第2个X表示输出参数是void
ps:
1、_beginthread需要__cdecl的线程函数地址,_beginthreadex和CreateThread需要__stdcall的线程函数地址。2、extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);
typedef int (__cdecl*FunPointer)(int a, int b);