函数调用约定,不同版本的系统或者不同的编译器大体没区别,在不同情况下有些区别,能区别就好。
函数调用约定:
_cdecl:C\C++默认的调用方式,调用方平衡栈,不定参数的函数可以使用这种方式。
_stdcall:被调方平衡栈,不定参数的函数无法使用这种方式。
_fastcall:寄存器方式传参,被调方平衡栈,不定参数的函数无法使用这种方式。
这个平衡栈,意思就是说,在子函数运行完成后,是由调用的一方释放栈存储空间,还是被调用的函数在返回之前平衡栈空间。
x86应用程序的函数调用有上面三种方式,但是x64应用程序只有1中调用方式,名为寄存器快速调用约定。前四个参数使用寄存器传递,多余的参数就放到栈空间中,参数的传递顺序从右到左,由函数调用方平衡占空间。前四个参数存放的寄存器是固定的表格如下。还有,任意大于8字节或者不是1字节,2字节,4字节,8字节的参数是通过引用传递的。
参数 | 整数类型 | 浮点类型 |
---|---|---|
第1个参数 | RCX | XMM0 |
第2个参数 | RDX | XMM1 |
第3个参数 | R8 | XMM2 |
第4个参数 | R9 | XMM3 |
这里举个例子,如果是void func(float , float , int , int)
那么前两个参数为XMM0,XMM1,后两个参数是R8,R9,而不是RCX,RDX。
这里值得注意的是,虽然前四个参数使用寄存器传递,但是相对应的栈空间(32位)仍然会预留。因为寄存器数量有限,一旦寄存器数量不够用,就可以把寄存器的值保存到预留的栈空间中。预留的空间也是由函数调用方平衡的。
平衡栈的操作有时会随着优化选项的不同而不同,有时是连续调用多个函数后,一次性平衡栈。调用方式不是固定的,不同的编译器也会存在一定区别,具体情况还是根据反汇编时看到的为准。
在调用类成员函数的时候,还会遇到 thiscall 的默认调用约定,所有成员函数(非静态成员函数)都有一个隐藏参数,即自身类型的指针。调用成员函数过程中,默认利用ecx中保存的对象首地址,并以寄存器传参的方式将其传递到成员函数中。 thiscall 的栈平衡方式与 __stdcall 相同,都是被调用方负责平衡栈。在类中,如果定义的时候就选择其它调用方式,则 this 指针不再使用 ecx 传递,而是改用栈传递参数。
另外,平衡栈的操作有时会随着优化选项的不同而不同,有时是连续调用多个函数后,一次性平衡栈。部分调用方式也不是固定的,不同的编译器也会存在一定区别,具体情况还是根据反汇编调试时看到的为准。