函数调用约定

最近在看《windows程序设计》一书,在书中看到使用windows函数的时候,要在函数前面加上WINAPI这样一个关键字。WINAPI 是在WINDEF.H中定义的,其定义如下

#define WINAPI __stdcall

其中__stdcall是一种函数调用的约定。

首先,这里要清楚的第一个问题是:什么是函数调用的约定?

通过在网上搜索,好搜百科给出了如下的定义:

函数调用约定,是指当一个函数被调用时,函数的参数会传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。

其中常用的函数约定有三种类型,分别是:

__pascal,

__stdcall,

__cdecl,

他们之间的区别如下表所示:

  参数传递顺序 谁负责清理参数占用的堆栈
__pascal 从左到有 调用者
__stdcall 从右到左 被调函数
__cdecl 从右到左 调用者

 

 

调用函数的代码和被调用函数必须采用相同的函数调用约定,程序才能正常运行。在Windows上,__cdecl是c/c++程序的缺省函数调用约定。

 

 

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

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

 

 

其中,VC默认使用__cdecl。所以如果需要使用__stdcall,可采用两种方法:

(1)可以在函数名前手工添加,只对单一函数有效

(2)直接修改工程属性(C/C++ > Advanced > Calling Convention)来一次性配置所有的函数

__cdecl可实现变长参数列表

__stdcall产生的代码更小

__cdecl的运行速度更快,这和内联函数有点类似,代码越多当然运行的越快

__cdecl主调用函数进行参数压栈并且恢复堆栈

__stdcall主调用函数进行参数压栈,被调函数恢复堆栈

所以如果使用__cdecl的函数多次调用同一函数,就产生多分恢复码

一份恢复码只能将一种长度的参数表出栈,所以要对不同长度的参数表堆栈恢复,必须要有多分恢复码,所以变长参数列表必须由主调函数恢复堆栈

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

__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为:_functionname

__stdcall通常用于DLL的创建(以支持多语言的调用),此外Win32API函数皆用__stdcall,所以Win32程序中的自定义函数也最好使用__stdcall

__cdecl非DLL的Console程序

你可能感兴趣的:(基础知识)