函数的调用惯例

转载自: http://www.cnblogs.com/jiqingwu/p/calling_conventions.html

什么是调用惯例

调用惯例(Calling Conventions)指计算机程序执行时调用函数或过程的一些约定,包括:

函数的参数是通过栈还是寄存器传递?
如果通过栈传递,顺序是怎样的,是从左至右入栈还是相反。
谁负责清理栈,是调用者还是被调用者?

从清理栈的角度来讲,调用惯例可分为三类:函数的调用者清理,函数清理,混合清理(有时由调用者清理,有时由函数自己清理)。

调用者清理

著名的cdecl就是由函数调用者清理栈的调用惯例。 cdecl是基于c语言的调用惯例,也是x86机器上大多数C编译器采用的调用惯例。

函数的返回结果多通过EAX寄存器返回。 对于32位机器,EAX能容纳4个字节。 整数或内存地址(指针),通过EAX寄存器返回是没有问题的。 超过4个字节的结构体呢?如何返回?

通过阅读 http://en.wikipedia.org/wiki/X86_calling_conventions,我找到了答案。 对于较小的结构体或对象,可以通过EAX:EDX寄存器对返回。 对于超大的对象或结构体,caller在调用函数之前会分配出内存空间,然后把这个空间地址作为第一个参数隐式地传给函数。被调用的函数callee把结果写进这片内存空间,再pop空间地址,之后才返回。 对于浮点数的结果,似乎是通过 ST0 x87 register(浮点寄存器)返回的。

因为调用者知道为参数分配了多少栈空间,所以由调用者清理栈就有一个好处: 为参数分配的栈空间大小可以动态决定。 因此cdecl支持可变参数的函数的调用,例如printf。

如果强迫某个函数使用cdecl调用惯例,可以在函数声明中加_cdecl关键字,如:

void _cdecl funct();

函数自己清理

pascal,stdcall,fastcall都是由函数来清理栈。 通过阅读程序的汇编代码,可以很容易识别这类调用惯例。因为函数返回前会清理栈。

  1. pascal是基于PASCAL编程语言的函数调用惯例。 参数按照从左到右的顺序压栈(和cdecl的入栈顺序相反)。 OS/21.x,Microsoft Windows 3.x 和 BorlandDelphi1.x中的16位API都使用这种函数调用惯例。
  2. stdcall是从pascal调用惯例演变出来的,和pascal不同的是,stdcall以从右到左的顺序对参数压栈。 返回值存储在EAX寄存器中。Win32 API就是采用的这种调用惯例。
  3. fastcall是混合使用寄存器和栈来存储函数的参数,比如把前两个参数存储在寄存器中,其余的参数入栈。 有Microsoft fastcall和Borland fastcall等不同的实现。

由函数自己清理栈的好处在于:调用者不需要每次调用函数之后都清理栈,从而节省了不少代码, 从而生成的二进制文件比较小。坏处在于,由于清理栈的代码是事先生成在函数体内, 所以不能支持可变参数的函数。

混合清理

混合清理的代表是thiscall,对C++中非静态成员函数使用的就是这种调用惯例。

对于gcc编译器来说,thiscall几乎和cdecl相同:函数调用者负责清理栈,参数按从右到左的顺序入栈。 不同的是,thiscall最后会把this指针压栈,就好象它是函数的第一个参数。(其实也是的吧)

对于Microsoft VC++编译器,thiscall类似于Windows API的stdcall,函数的参数从右到左压栈,由参数来清理栈。和stdcall不同的是,thiscall会通过ECX寄存器来传递this指针。因为由函数自己清理栈不支持可变参数的函数调用,所以对于可变参数的函数,则由函数的调用者来清理栈。这是thiscall的灵活之处。

总结

调用惯例 出栈方 参数传递 名字修饰
cdecl 函数调用方 从右至左的顺序压参数入栈 下划线+函数名
pascal 函数本身 从左至右的顺序入栈 较为复杂,参见pascal文档
stdcall 函数本身 从右至左的顺序压参数入栈 下划线+函数名+@+参数的字节数, 如函数 int func(int a, double b)的修饰名是 _func@12
fastcall 函数本身 头两个 DWORD(4字节)类型或者更少字节的参数 被放入寄存器,其他剩下的参数按从右至左的顺序入栈 @+函数名+@+参数的字节数
thiscall 不一定 从右至左的顺序压参数入栈(有时会通过寄存器传递this指针) 不详

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值