函数调用约定

本文详细介绍了x86架构下的__cdecl、__stdcall和__fastcall三种调用约定,包括它们的栈平衡方式、参数传递规则及函数命名规范。在x64调用约定中,使用了更多的寄存器传递参数,以提高效率。这些约定对于理解和优化C/C++程序的性能至关重要。
摘要由CSDN通过智能技术生成

一、 调用约定

函数调用约定是应用程序二进制接口的一部分,描述了执行函数时返回值和参数入栈的方式,不同公司开发的C语言编译器都有不同的ABI标准,这些调用约定差异很大,如果调用约定不相同,那么程序是无法正常进行工作的。

二、x86位调用约定

随着IBM兼容机对市场进行洗牌后,微软的操作系统和编程工具占据了市场的主导地位,但是还是有少数公司和GNU在维护着自己的调用约定,除了编译器的调用约定之外,不同的解释语言的解释器也执行着对应的调用约定。

2.1 __cdecl

__cdecl 是 C 和 C++ 程序的默认调用约定

栈平衡方式由调用者完成
入栈顺序从右到左
返回值传递由EAX/AX/AL传递
浮点型结果存放在寄存器ST0
函数命名下划线字符 (_) 作为名称的前缀

2.2 __stdcall

__stdcall调用约定用于调用 Win32 API 函数,是32位模式下常用的三种标准之一。

栈平衡方式由被调者完成
入栈顺序从右到左
返回值传递由EAX/AX/AL传递
浮点型结果存放在寄存器ST0
函数命名下划线 (_) 的名称为前缀
该名称后跟符号 (@) 后跟参数的字节数
因此,声明为 int func( int a, double b ) 的函数按如下所示进行修饰:_func@12

2.3 __fastcall

__fastcall从名字就可以看出他的速度比前面两种调用方式更快,因为他使用了两个寄存器传递参数,多出的参数才使用栈传递,寄存器传参数不需要入栈出栈操作,并且寄存器速度远高于内存,因此该方式的速度更快。

栈平衡方式由被调者完成
入栈顺序在自变量列表中按从左到右的顺序找到的前两个 DWORD 或更小自变量将在 ECX 和 EDX 寄存器中传递
所有其他自变量在堆栈上从右向左传递
返回值传递由EAX/AX/AL传递
浮点型结果存放在寄存器ST0
函数命名At 符号 (@) 是名称的前缀
参数列表中的字节数(在十进制中)前面的 at 符号是名称的后缀。

从上面三种方式可以看出函数调用时栈传递方式有两种,一种是栈传递参数,另一种是寄存器传递参数,在系统调用时会从用户态转换成内核态,x86架构下会从TSS中查找对应特权级的栈,用户栈和内核栈是不同的两段内存,且内核栈与用户栈是隔离开的,要想让内核获取到参数,需要进行数据拷贝,也就意味着增加额外的工作量,效率也就降低了,不可能采用这种方式传递参数,因此使用寄存器传递参数是最快的方式。

三、x64调用约定

默认情况下,x64 应用程序二进制接口 (ABI) 使用四寄存器 fast-call 调用约定。 系统在调用堆栈上分配空间作为影子存储,供被调用方保存这些寄存器。

整数参数在寄存器 RCX、RDX、R8 和 R9 中传递。 浮点数参数在 XMM0L、XMM1L、XMM2L 和 XMM3L 中传递。 16 字节参数按引用传递。

对于原型函数,在传递参数之前,所有参数都将转换为所需的被调用方类型。 调用方负责为被调用方的参数分配空间。 调用方必须始终分配足够的空间来存储 4 个寄存器参数,即使被调用方不使用这么多参数。 此约定简化了对非原型 C 语言函数和 vararg C/C++ 函数的支持。 对于 vararg 或非原型函数,任何浮点值都必须在相应的通用寄存器中重复。 调用之前,必须将除前 4 个参数外的其他参数存储在影子存储后面的堆栈中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值