C++愤恨者札记3——函数调用约定
函数调用约定指的是,参数压栈顺序及弹栈位置的约定。这个约定在函数声明时指定,如:
void __stdcall Fn(int arg1, int arg2);
其中__stdcall就是调用约定,表示参数从右至左入栈,而函数自己负责参数弹栈工作。
还有一种常用约定为__cdecl,表示参数从右至左入栈,而函数调用者负责参数弹栈工作。
如果没有指定调用约定,编译器会使用默认约定。VS中默认约定可以在工程属性中设置:
VC++中,一般函数使用__cdecl约定,但类成员函数,包括构造函数,使用__stdcall约定(我不知道为什么),从它们的反汇编代码中可以看出。
源码:
class Node
{
public:
Node(){}
void Fn( int a, int b ){}
};
void Fn(int a, int b)
{
}
void main()
{
Node n;
n.Fn(1, 2);
Fn( 1, 2);
}
main与Fn反汇编代码如下:

-------------------------------------------------------------------------------------------------------------------

函数调用约定在汇编中,区别是巨大的,而在高级语言里却很少进入视野。下面给出一个因约定不一致导致程序崩溃的例子。程序要在 debug 版本下编译,或 release 版下把 /Od 打上,禁止优化,否则可能看不到程序崩溃。
typedef void (__cdecl *CDeclType)(void*);
void __stdcall Fn(void* p)
{
}
void MyCallBack( CDeclType pFn )
{
pFn(0); /*pFn的参数将会被两次弹栈,
破坏栈平衡MyCallBack将找不到返回地址*/
}
void main()
{
MyCallBack( (CDeclType)Fn );
}