windows x64位系统函数调用如何传递参数

在 x64 系统(Windows)中,函数调用默认使用一种快速调用(Fastcall)约定。它主要依靠寄存器来传递前几个参数,以提高效率,只有参数过多时才会使用堆栈。

它的核心规则可以概括为一张表:

参数位置参数类型传递使用的寄存器
第1个参数整数/指针RCX
浮点数XMM0
第2个参数整数/指针RDX
浮点数XMM1
第3个参数整数/指针R8
浮点数XMM2
第4个参数整数/指针R9
浮点数XMM3
第5个及以后所有类型堆栈(从右到左压栈)

详细规则与特殊情况

  • 整数与浮点参数的“专车专用”:前四个整数参数和浮点参数使用的是两套完全独立的寄存器,互不干扰。例如,当一个函数的前两个参数是intfloat时,它们会分别使用RCXXMM1寄存器。

  • 大型参数(结构体等)通过引用传递:参数大小不是 1、2、4 或 8 字节,或者本身是__m128类型、数组和字符串时,不会尝试直接放入寄存器,而是在内存中复制一份,然后将指向这份拷贝的指针放入对应的寄存器或堆栈中。这可以理解为“传引用”。

    • 例外:大小恰好是 8、16、32 或 64 位的结构体,会像整数一样,直接按值传递到对应的寄存器中。

  • “影子空间”(Shadow Store):一个很有趣的约定。调用方在堆栈上始终要预留 32 字节(4个寄存器 × 8字节)的空间,即使被调用的函数用不到这么多参数。这主要是为了在需要时,被调用方可以方便地将寄存器参数保存回堆栈,简化了对可变参数(varargs)等复杂情况的支持。

  • 寄存器保存规则:在整个调用过程中,一些寄存器的值需要被被调用方(Callee)保护(即使用前先保存,返回前恢复),如RBX,RBP,RDI,RSI,R12-R15,XMM6-XMM15。而另一些则被认为是易失的(Volatile)调用方(Caller)可以假设它们在函数返回后已被修改,包括用于传参的RCX,RDX,R8,R9,XMM0-XMM5以及RAX,R10,R11等。这保证了函数调用后关键数据不会丢失。