我们知道,函数调用是靠栈来实现的,编译器帮助我们做了压栈和恢复堆栈工作,使我们在调用任一个函数时,都不用进行压栈和恢复堆栈工作。而又由于实现的方式很多,那我们怎么告诉编译器按照哪种方式呢?
答案是函数调用约定(function call convention).
函数调用约定(function call convention)是什么?
函数调用约定本质上是告诉编译器怎么把高级语言编译成汇编代码。
这里编译器要解决几个问题:
1 决定参数的压栈顺序的问题。如果函数的参数多于一个,是按照从右向左的顺序压栈,还是其它。
2 谁来负责函数调用堆栈恢复的问题。是调用者还是函数自身去恢复?
首先我们先来看一下cdecl方式,这是C/C++语言的默认处理方式。cdecl告诉编译器,参数要从右向左压栈,并且调用者负责恢复堆栈(正式因为这个,才能实现可变参数,support variable parameter length,参考http://blog.csdn.net/hongchangfirst/article/details/8765549,也是因为这个,二进制体积比较大)。
在C/C++中,我们可以这样使用cdecl
int __cdecl f(int a, int b);
其实,cdecl可以省略,因为这是默认的方式。
对于stdcall方式,这是pascal的处理方式,stdcall告诉编译器,参数要从右向左压栈,并且函数自身恢复堆栈。
对于函数f,我们可以改变其调用约定:
int __stdcall f(int a, int b);
至于stdcall,还有另外一项说明,即告诉编译器怎么处理函数名:前加下划线,后加@,之后跟参数总大小,如上述函数f,就变成了_f@8。
cdecl和stdcall最大的区别就是cdecl是由调用者恢复堆栈,而stdcall由函数自身恢复堆栈。它们都是函数调用约定的一种方式。
常见的其它函数调用约定有:
fastcall
thiscall
fastcall和stdcall差不多,区别是,fastcall规定第一第二的比双字节小的参数通过寄存器传递,而不通过压栈的方式。
thiscall是C++中成员函数默认的调用约定。由于成员函数还有一个this指针,它也是参数的一部分,所以必须特殊处理。
它是这样的方式:
1 参数从右向左入栈。
2 如果参数个数确定,this指针通过ecx传递给函数,并且函数自己恢复堆栈,类似stdcall方式。
3 如果参数个数不确定,this指针在所有参数压栈后被压入堆栈,这相当于 T* const this是第一个参数,调用者恢复堆栈,类似cdecl方式。
转载请注明出处:
原文:http://blog.csdn.net/hongchangfirst/article/details/8765549
作者:hongchangfirst