函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,例如 :
调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。在Windows上,__cdecl是C/C++程序的缺省函数调用约定。 在有的cpu上,编译器会用寄存器传递参数,函数使用的堆栈由被调函数分配和释放。这种调用约定在行为上和__cdecl有一个共同点:实参和形参数目不符不会导致堆栈错误。 不过,即使用寄存器传递参数,编译器在进入函数时,还是会将寄存器里的参数存入堆栈指定位置。参数和局部变量一样应该在堆栈中有一席之地。参数可以被理解为由调用函数指定初值的局部变量。 _stdcall与_cdecl的不同 a. 默认支持:VC默认使用_cdecl。所以如果需要使用_stdcall,可采用两种方法:(1)可以在函数名前手工添加,只对单一函数有效 (2)直接修改工程属性(C/C++ > Advanced > Calling Convention)来一次性配置所有的函数 b. 功能不同: _cdecl可实现变长参数列表 c. 代码大小:_stdcall更小
d.
速度不同:
e.
谁负责恢复堆栈:_cdecl主调用函数进行参数压栈并且恢复堆栈;_stdcall主调用函数进行参数压栈,被调函数恢复堆栈;这也正是产生 f. 产生的函数名不同: _stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。_cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。 g. 使用范围:
_stdcall:通常用于DLL的创建(以支持多语言调用);此外Win32
API函数皆用_stdcall(比如MessageBox),所以Win32程序中的自定义函数也做好使用_stdcall。
跨语言调用
函数调用约定只是“调用函数的代码”和被调用函数之间的关系。
假设函数A是__stdcall,函数B调用函数A。你必须通过函数声明告诉编译器,函数A是__stdcall。编译器自然会产生正确的调用代码。 如果函数A是__stdcall。但在引用函数A的地方,你却告诉编译器,函数A是__cdecl方式,编译器产生__cdecl方式的代码,与函数A的调用约定不一致,就会发生错误。 以delphi调用VC函数为例,delphi的函数缺省采用__pascal约定,VC的函数缺省采用__cdecl约定。我们一般将VC的函数设为__stdcall,例如:
int __stdcall add(int a, int
b);
在delphi中将这个函数也声明为__stdcall,就可以调用了:
function add(a: Integer; b:
Integer): Integer;
stdcall; external ‘a.dll’; 因为考虑到可能被其它语言的程序调用,不少API采用__stdcall的调用约定。 |
|