调用约定主要规定了两个方面的内容: 1. 函数之间如何传递参数 2. 谁来保存寄存器
32位平台
C语言默认的调用约定叫做cdecl,规定了使用栈来传递参数,按照从右到左的顺序依次入栈。被调函数中不会知道 调用者到底传了多少个参数,所以栈的平衡由调用者完成,这种调用约定支持可变参数
stdcall也是一种调用约定,同样是按从右到左的顺序依次将参数入栈。与cdecl不同的是栈的平衡由被调函数完 成,函数定义了多少个参数调用者就需要传递多少个参数,否则就会出错
fastcall和stdcall更像,除了前两个参数使用ecx和edx寄存器传递。当参数个数小于等于两个时,不会涉及到 内存读写,所以速度上更快
gcc可以使用__attribute__关键字来改变函数的调用约定,如__attribute__((stdcall)),但是当函数被定义 为可变参数时,只会使用cdecl
寄存器可分为两大类,volatile和non-volatile。调用者需要在调用一个函数的前后寄存器的内容保持不变,对 于前者则需要调用者自己对相应的寄存器进行管理(通常是入栈),而后者被调函数需要保证其内容在被调函数执行 前后不会变。
volatile 寄存器:eax,ecx,edx non-volatile 寄存器: 其他寄存器
整型返回值(包括指针)通常都是保存在eax寄存器
64位平台
64位平台上调用约定只有fastcall一种,但是与32位的fastcall不一样,且不同的操作系统之前也有区别,只说linux平台。 前6个参数按从右到左顺序由RDI,RSI,RDX,RCX,R8,R9寄存器传递,更多的参数通过栈传递。栈平衡由调用者 来做。 volatile 寄存器:RDI,RSI,RDX,RCX,R8,R9,RAX,R10,R11 默认情况下函数开始会压入RBP, -fomit-frame-pointer选项改变这一行为