You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MSVC x64函数调用参数传递顺序不符预期求助

x64汇编参数传递顺序疑惑解答

我正在学习x64汇编,使用MSVC工具集编写了基础示例程序。根据微软文档,整数和指针类型的前4个参数会通过RCX、RDX、R8、R9寄存器传递,剩余参数则使用栈传递。我理解这一规则,且从下方代码片段和调试反汇编中也能看出这一点,但无法理解为何参数传递没有按预期顺序进行:我原本预期反汇编会以从右到左的顺序(e、d、c、b、a、cp、ip)传递参数,但实际看到的是e、b、a等顺序,恳请有人帮忙解释,谢谢!

代码片段

__declspec(noinline) int testArgsAsm(int* x, char* cp, int a, int b, int c, int d, int e) {
    std::cout << *x << *cp;
    int sum = a + b + c + d + e;
    if (sum == 0) std::cout << "0";
    else std::cout << "Non 0";
    return sum;
}

int main(int argc, const char** args) {
    int a, b, c, d, e;
    int* ip = new int;
    *ip = 0x101;
    char* cp = new char;
    *cp = ('g');
    std::cin >> a >> b >> c >> d >> e;
    testArgsAsm(ip, cp, a,b,c,d,e);
    return 0;
}

反汇编及寄存器快照

Assembly testArgsAsm(ip, cp, a,b,c,d,e);
00007FF694DB1359 mov eax,dword ptr [e]
00007FF694DB135D mov r9d,dword ptr [b]
00007FF694DB1362 mov r8d,dword ptr [a]
00007FF694DB1367 mov dword ptr [rsp+30h],eax
00007FF694DB136B mov eax,dword ptr [d]
00007FF694DB136F mov dword ptr [rsp+28h],eax
00007FF694DB1373 mov eax,dword ptr [c]
00007FF694DB1377 mov rdx,rbx
00007FF694DB137A mov rcx,rdi
00007FF694DB137D mov dword ptr [rsp+20h],eax
00007FF694DB1381 call testArgsAsm (07FF694DB1270h)

<= Instruction pointer registers at this instant
RAX = 0000000000000003
RBX = 0000025633A16030
RCX = 0000025633A13EC0
RDX = 0000025633A16030
RSI = 0000000000000000
RDI = 0000025633A13EC0
R8 = 0000000000000001
R9 = 0000000000000002
R10 = 000000000000000F
R11 = 000000F5016F0001
R12 = 0000000000000000
R13 = 0000000000000000
R14 = 0000000000000000
R15 = 0000000000000000
RIP = 00007FF694DB1381
RSP = 000000F5016FFB00
RBP = 0000000000000000
EFL = 00000202

问题解答

哈哈,刚学x64汇编遇到这种“参数顺序混乱”的疑惑太正常了!其实你是把编译器准备参数的操作顺序和调用约定要求的参数最终位置搞混了,咱们一步步拆解:

首先明确testArgsAsm的参数和MSVC x64调用约定的对应关系:

  • 第1个参数int* x → 对应RCX寄存器
  • 第2个参数char* cp → 对应RDX寄存器
  • 第3个参数int a → 对应R8寄存器
  • 第4个参数int b → 对应R9寄存器
  • 第5个参数int c → 栈上rsp+20h位置
  • 第6个参数int d → 栈上rsp+28h位置
  • 第7个参数int e → 栈上rsp+30h位置

调用约定只要求最终调用函数时,寄存器和栈里的参数位置完全匹配上述规则,根本不限制编译器准备这些参数的操作顺序!编译器会根据寄存器使用情况、内存访问效率来灵活调整准备顺序,怎么高效怎么来。

看你给出的反汇编就能验证这一点:

  1. 先把e读到eax,存到栈上rsp+30h(对应第7个参数的位置)
  2. 把b放到R9d(对应第4个参数的寄存器)
  3. 把a放到R8d(对应第3个参数的寄存器)
  4. 把d存到栈上rsp+28h(对应第6个参数的位置)
  5. 把c存到栈上rsp+20h(对应第5个参数的位置)
  6. 最后把ip(第1个参数)放到RCX,cp(第2个参数)放到RDX

再看你提供的寄存器快照,完全符合约定:

  • RCX是ip的值,RDX是cp的值,R8是a,R9是b
  • 栈上的rsp+20h、28h、30h分别对应c、d、e

你之前误以为要“从右到左依次传递参数”,其实那是老的x86 cdecl调用约定里的入栈规则,但x64下MSVC的调用约定已经完全不同了:前4个参数用寄存器,栈上的参数是按参数定义的从左到右顺序排列(第5个参数在栈的最低地址,依次往上),而编译器准备参数的顺序完全不需要死板遵循右到左,只要最终位置正确就行。

内容的提问来源于stack exchange,提问作者Abhinav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:43:09