fastcall, thiscall, cdecl, stdcall 调用约定
调用约定规定了函数参数如何传递、栈如何管理以及函数调用后栈的清理方式。常见的调用约定包括fastcall、thiscall、cdecl、stdcall,它们在不同平台和架构下的实现方式有所不同。
特别说明,不同cpu,编译器架构调用约定不一样,下面只适用 x86_64 CPU架构 和 MSVC编译器
fastcall
fastcall 是一种优化的调用约定,旨在减少栈操作,加快函数调用的速度。不同平台下的实现差异主要体现在寄存器的使用上。
符号名
- C: @
funcName@paramSize - C++: 较为复杂, 一般和
C相比funcName变为_Z后面跟函数名长度和函数名, 遍历命名空间和类也是长度加名字, 再跟参数类型, 不需要特别记忆, 导出一般用extern C- 详细信息: http://web.mit.edu/tibbetts/Public/inside-c/www/mangling.html
- 例如: @
fun1@0 - 变为: @
_Z4fun1v@0- 返回类型
void用v表示
- 返回类型
32位系统(x86架构)
- 参数传递: 前两个函数参数通过
ECX和EDX寄存器传递。- 第一个参数传递到
ECX。 - 第二个参数传递到
EDX。
- 第一个参数传递到
- 栈传递: 第三个及后续参数通过栈从右到左依次传递。
- 栈清理: 调用者负责清理栈。
这种方式减少了栈操作,提升了性能,特别适用于传递少量参数的函数调用。
64位系统(x64架构)
在x64架构下,fastcall已成为默认调用约定,优化寄存器的使用:
- 整数和指针参数传递: 前四个参数分别通过
RCX、RDX、R8、R9寄存器传递。 - 浮点参数传递: 浮点数类型参数通过
XMM0至XMM3寄存器传递。 - 栈传递: 超过四个参数则通过栈传递。
- 栈清理: 被调用者负责清理栈,栈帧必须保持16字节对齐,以支持SSE和AVX指令集。
区别总结
- 32位: 使用
ECX和EDX传递前两个参数,栈清理由调用者完成。 - 64位: 使用
RCX、RDX等寄存器传递前四个参数,栈清理由被调用者完成,且需要保持16字节对齐。
thiscall
thiscall 是 C++ 类的非静态成员函数的专用调用约定,它用于传递类的this指针。根据不同的系统架构,thiscall的实现有所不同。
符号名
- C: _
funcName - C++: 略…
32位系统
this传递:this指针通过ECX寄存器传递。- 其余参数: 通过栈传递,类似于其他调用约定。
64位系统
- 标准调用约定: 在64位系统中,不再有单独的
thiscall调用约定。this指针会使用标准的寄存器传递,通常是RCX,根据默认的64位调用规则处理。
特性总结
thiscall 是用于非静态成员函数的调用约定,this指针被隐式传递给成员函数。32位系统中使用ECX寄存器传递,而64位系统中则采用统一的调用规则,不再单独定义thiscall。
cdecl
cdecl 是C语言和C++中最常见的调用约定,几乎所有函数默认都会使用此约定。
符号名
- C: _
funcName - C++: 略…
特性
- 参数传递: 所有参数通过栈从右到左依次入栈。
- 栈清理: 调用者负责清理栈。
这是最常见的调用约定,适用于大多数平台和编译器。调用者清理栈使得它在支持可变参数函数(如printf)时表现良好。
示例
1 | int __cdecl python_rocks(int one, int two, int three); |
对应的汇编代码:
1
2
3
4
5
push three
push two
push one
call python_rocks
add esp, 12
1 | push three |
stdcall
stdcall 是 Windows API 的默认调用约定,专为减少函数调用的复杂性设计。
符号名
- C: _
funcName@paramSize - C++: 略…
特性
- 参数传递: 所有参数通过栈从右到左传递。
- 栈清理: 由被调用者负责清理栈,而非调用者。
stdcall 在 Windows 平台上广泛使用,简化了调用者的责任,使代码更加简洁。
示例
1 | int __stdcall python_rocks(int one, int two, int three); |
对应的汇编代码:
1
2
3
4
5
6
push three
push two
push one
call python_rocks
;; 被调用者清理栈
ret 12
1 | push three |
MSDN x64 ABI 约定概述
具体可以参考MSDN:
在64位系统上,MSDN ABI 调用约定规定了参数通过寄存器传递的方式和栈帧对齐规则。遵循这些规则可提高代码的跨平台兼容性和性能。
总结
调用约定定义了函数调用过程中参数传递和栈管理的方式。在32位和64位系统之间,这些约定存在显著差异。fastcall和thiscall在不同平台下的寄存器利用有所不同,cdecl 和 stdcall 则在栈清理方式上有所差异。了解这些约定对编写高效、跨平台的代码至关重要。
install_url to use ShareThis. Please set it in _config.yml.