函数调用约定
2013-01-22 22:05
281 查看
函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,例如 :
调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。在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的调用约定。
参数传递顺序 | 谁负责清理参数占用的堆栈 | |
__pascal | 从左到右 | 调用者 |
__stdcall | 从右到左 | 被调函数 |
__cdecl | 从右到左 | 调用者 |
在有的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的调用约定。
相关文章推荐
- CPP 函数调用约定
- 函数调用约定
- 函数调用约定
- C/C++ 函数的编译方式与调用约定以及extern “C”的使用
- 函数调用约定_stdcall[转]
- 函数调用约定 stdcall cdecl fastcall thiscall naked call
- 函数调用约定
- 函数调用约定
- 关于函数的调用约定
- Windows下的函数调用约定
- 函数调用约定
- 函数调用约定和堆栈
- 函数调用约定和堆栈
- C++ 函数调用约定和名称修饰
- 函数调用约定:__cdecl,__stdcall,__fastcall,__thiscall
- 函数调用约定
- 函数调用约定
- 函数调用约定
- 逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.
- 函数签名-调用约定-函数调用栈