函数调用方式【2】

在进行函数调用时,有几种调用方法,主要有__cdecl,__stdcall,__fastcall,__thiscall
,__clrcall,naked call。它们决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法。

1、__stdcall调用约定:用于调用Win32 API函数。函数的参数自右向左通过栈传递,除非参数是指针或引用,否则是按值传递。在主调用函数中负责压栈,在被调用函数中负责弹出堆栈中的参数,并负责恢复堆栈。不能实现变参函数。采用这种调用方式的函数需要一个函数原型。如果函数是可变参数的,则编译器会将函数的调用方式改为__cdecl调用。
2、__cdecl调用约定:是C和C++程序的缺省调用方式。函数参数的压栈顺序是从右到左,由主调用函数进行参数压栈并且恢复堆栈。由于主调用函数管理堆栈,所以可以实现变参函数。由于每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。
3、__fastcall调用约定:其调用的主要特点就是快,因为在尽可能的情况下,它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送),被调用的函数在返回前清理传送参数的内存栈。
4、__thiscall调用约定:应用于"C++"成员函数,并且是不采用可变参数的C++成员函数的默认调用方式。函数参数的压栈顺序是从右到左,被调用者清理堆栈,所以不能实现可变参数。在x86系统中,this指针存放于CX/ECX寄存器中,而不是栈中。使用__thiscall的原因之一是当成员函数的默认调用方式是__clrcall的时候,使用__thiscall可以使某个成员函数可以被本地代码调用。
5、__clrcall调用约定:它与.NET Framework有关系,用于指定函数只能被托管代码(managed code)调用。这样编译器就不会生成原生的入口点了。
6、naked call调用约定:这是VC里一种给高级用户使用的调用约定,它实际上就是没有规范,用户可以通过内嵌汇编来实现任意想要的调用约定。naked call不是类型修饰符,故必须和__declspec共同使用。
7、过时的调用约定:__pascal,__fortran和__syscall调用方式现在已不支持。可以用现今支持的函数调用方式配合合适的链接器选项来实现同样的功能。WINDOWS.H现在支持WINAPI宏,它可以为目标转换成合适的调用方式。

备注:
1、关键字__stdcall、__cdecl和__fastcall可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
2、调用约定可以通过工程设置:如在VS2008中,Project->Properties->Configuration Properties->C/C++->Advanced->Calling Convention。默认为__cdecl(/Gd)。

函数名字修饰约定:
a、C编译时函数名修饰约定规则:
1、__stdcall调用约定:在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的总字节数(10进制),格式为,不进行大小写转换。如:void Input(int &m,int &n),被修饰成:。
2、__cdecl调用约定:仅在输出函数名前加上一个下划线前缀,格式为_functionname,不进行大小写转换。
3、__fastcall调用约定:在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number,不进行大小写转换。
b、C++编译时函数名修饰约定规则:
1、__stdcall调用约定:以"标识参数表的开始,后跟参数表;参数表以代号表示:
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"标识整个名字的结束,如果该函数无参数,则以"XZ"标识结束。
其格式为""或""。
如:int Test1(char *var1,unsigned long),被修饰成:
  void Test2(),被修饰成:
2、__cdecl调用约定:规则同上面的__stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。
3、__fastcall调用约定:规则同上面的__stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。

注意:
1、_beginthread需要__cdecl的线程函数地址,_beginthreadex和CreateThread需要__stdcall的线程函数地址。
2、一般WIN32的函数都是__stdcall。而且在Windef.h中有如下的定义:
#define CALLBACK __stdcall
#define WINAPI __stdcall
3、对于extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);
修饰符的书写顺序应该为:
typedef int (__cdecl*FunPointer)(int a, int b);

转自:http://hi.baidu.com/baiyw920/item/bc81663c2f81c88df5e4adad

你可能感兴趣的:(函数调用方式【2】)