您的位置:首页 > 其它

函数调用约定

2013-01-22 22:05 281 查看
函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,例如 :
参数传递顺序谁负责清理参数占用的堆栈
__pascal从左到右调用者
__stdcall从右到左被调函数
__cdecl从右到左调用者
调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。在Windows上,__cdecl是C/C++程序的缺省函数调用约定

在有的cpu上,编译器会用寄存器传递参数,函数使用的堆栈由被调函数分配和释放。这种调用约定在行为上和__cdecl有一个共同点:实参和形参数目不符不会导致堆栈错误。

不过,即使用寄存器传递参数,编译器在进入函数时,还是会将寄存器里的参数存入堆栈指定位置。参数和局部变量一样应该在堆栈中有一席之地。参数可以被理解为由调用函数指定初值的局部变量。

_stdcall与_cdecl的不同

a.默认支持:VC默认使用_cdecl。所以如果需要使用_stdcall,可采用两种方法:(1)可以在函数名前手工添加,只对单一函数有效(2)直接修改工程属性(C/C++ > Advanced >Calling Convention)来一次性配置所有的函数

b. 功能不同:_cdecl可实现变长参数列表

c. 代码大小:_stdcall更小

d.速度不同: _cdecl更快(代码更多当然意味着运行更快,有点像内联函数)

e.谁负责恢复堆栈:_cdecl主调用函数进行参数压栈并且恢复堆栈;_stdcall主调用函数进行参数压栈,被调函数恢复堆栈;这也正是产生

a).不同代码大小的原因:如果使用_cdecl的函数多次调用同一函数,就要产生多份恢复码。

b).功能不同的原因:实现变长参数列表。一份恢复码只能将一种长度的参数表出栈,所以要对不同长度的参数表堆栈恢复,必须要有多份代码,所以变长参数必须有主调函数恢复(所以是_cdecl)。

f. 产生的函数名不同:

_stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。_cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。

g. 使用范围:

_stdcall:通常用于DLL的创建(以支持多语言调用);此外Win32API函数皆用_stdcall(比如MessageBox),所以Win32程序中的自定义函数也做好使用_stdcall。

_cdecl:非DLL的console程序。


跨语言调用
函数调用约定只是“调用函数的代码”和被调用函数之间的关系。

假设函数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, intb);

在delphi中将这个函数也声明为__stdcall,就可以调用了:

function add(a: Integer; b:Integer): Integer;

stdcall; external ‘a.dll’;

因为考虑到可能被其它语言的程序调用,不少API采用__stdcall的调用约定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: