函数的调用协定

函数的调用协定(Calling Convention)包括了定义如何传递参数,接收计算结果,如何维持程序的上下文状态(context)和清理栈。我们写一个函数的完整模式应该为:

BOOL WINAPI IsDebuggerPresent(VOID);

其中WINAPI是调用协定的名称,如果没有调用协定,那么编译器会根据语言环境使用默认的调用协定。

常用的调用协定包括:C调用协定、标准调用协定、快速调用协定和C++成员使用的this调用协定。

C调用协定是C/C++程序的普通函数所默认调用协定,完全使用栈来传递参数,函数调用者(caller)在发起调用前将参数按从右到左依此压入到栈中,并且负责在函数返回后调整栈指针清除压入栈的参数。从右到左压入参数的好处是对于被调用函数来说第一参数相对栈顶的偏移总是固定的,而且是调用者负责清理参数,所以C调用协定支持可变数目的参数。

标准调用协定与C调用协定很类似,也是用栈来传递参数,传递顺序也是从右到左。但不同的是,标准调用协定规定被调用函数清理栈中的参数。对于被频繁调用的函数,这样的好处是可以减小目标程序的大小,因为清理栈的指令不必反复出现在调用函数中,标准调用协定使用的关键字是__stdcall.

大多数windows系统函数和API使用的都是标准调用协定,只不过通常是是使用__stdcall关键字的别名。从文件windef.h中我们能够发现WINAPI和CALLBACK关键字其实就是__stdcall.

大多数Windows API和回调函数使用的都是标准调用协议,所有使用标准调用协议的函数必须有函数原型。

快速调用协定(fastcall)使用协定使用寄存器传递参数,因此比使用栈速度更快。对于32位程序,快速调用协定使用ECX和EDX来传递前两个(左起)长度不超过32位的参数,其他参数使用栈来传递(从右到左)。举例来说,如果第一个参数(左起)是__int64,第二个参数是int,第三个参数是char,那么第一个参数就使用栈传递,第二个参数会用ECX传递,第三个参数会用EDX来传递。

在C++程序中的成员函数默认使用的调用协定是this调用协定,这种调用协定的最重要的特征就是this指针会被放入ECX寄存器传递给被调用的方法。This 调用协定也是要求被调用函数负责清理栈,因此不支持可变变量的参数。当我们在C++类中定义了可变数量参数的成员函数时,编译器(VC)会自动改为使用C调用协定。当调用这样的方法时,编译器会将所有参数压入栈之后,再将this指针压入栈。

VC8以前的VC编译器没有为this调用协定定义关键字,因此程序员不能显示地指定this调用协定,VC8加入了__thiscall作为this调用协定的关键字,因为托管的C++默认使用新定义的CLR调用协定,如果要改为使用传统的this调用协定,那么就需要显示的在声明加上__thiscall关键字。

CLR调用协定:在VC8以前,编译器会为每个托管函数生成两个入口点,一个是托管(managed)入口点,一个是本地(native)入口点。这样当托管代码使用本地入口点调用该函数时(虚拟函数必须总使用本地入口点调用),本地入口点需要将调用转给托管入口点,这样便会造成不必要的托管/非托管上下文切换和参数/返回值的复制,这种现象被称为双重转换(double thunking)。因为双重转换会导致性能损失,所以Visual Studio 2005引入了一个新的调用约定关键字,如果一个托管函数不会被非托管代码使用指针调用,那么可以在声明此函数时用新增的__clrcall修饰符阻止编译器生成两个入口。

因为使用传统的DLL输出方法(dllexport)输出一个托管函数也导致编译器为其产生两个入口点,而且任何通过DLL方法对该函数的调用都会使用本地入口点(再转给托管入口点)。为了防止这种情况的双重转换,SDK的文档建议使用.Net模块引用方法,不要用传统的DLL方法。

X64调用协定是针对64位系统的,只使用一种派生自快速调用的调用协定,使用寄存器传递前四个参数,其他参数使用栈来传递。

对于函数声明中没有指定调用协定的函数,编译器会根据情况使用合适的默认的调用协定,也可以通过编译选项来指定调用协定

/Gd, 默认设置,使用C调用协定(__cdecl)作为默认调用协定

/Gr, 使用快速调用协定(__fastcall)作为默认的调用协定

/Gz, 使用标准调用协定(__stdcall)作为默认调用协定。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值