首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言函數呼叫參數入棧順序:x86 vs x64、呼叫慣例深度解析

     編輯:關於C語言

參數入棧順序不是固定的

許多面試題問「參數是從左到右還是右到左壓棧?」標準答案是:**視架構與呼叫慣例而定**。

實測代碼

#include <stdio.h>

void test(int a, int b) {
    printf("&a = %p\n", (void*)&a);
    printf("&b = %p\n", (void*)&b);
    if ((uintptr_t)&a > (uintptr_t)&b)
        puts("Left-to-right (a 位址較高 → 先壓入)");
    else
        puts("Right-to-left (b 位址較高 → 先壓入)");
}

int main(void) { test(1, 2); return 0; }

實測結果對比

平台呼叫慣例入棧順序說明
32-bit x86 (Linux/Windows)cdecl右到左呼叫者清理堆疊,方便可變參數
64-bit x86-64 (Linux, System V AMD64 ABI)System V AMD64左到右 (概念上)前 6 整數參數放暫存器 (RDI, RSI, RDX, RCX, R8, R9),其餘才壓棧;棧上順序與宣告順序一致
64-bit x86-64 (Windows, Microsoft x64)Microsoft x64左到右前 4 參數放暫存器 (RCX, RDX, R8, R9),影子空間預留

為什麼 64 位元看起來是「左到右」?

以 System V AMD64 ABI 為例:

  1. 呼叫端將前 6 個整數參數依序放入 RDI, RSI, RDX, RCX, R8, R9
  2. 被呼叫端序言(prologue)將這些暫存器**依序**存入棧框架(RBP 相對位移)
  3. 因此棧上分佈順序與參數宣告順序一致,但實際傳遞是經由暫存器**

反組譯證據 (GCC 64-bit Linux)

; 呼叫端 main
mov esi, 2      ; 第二參數 → RSI
mov edi, 1      ; 第一參數 → RDI
call test

; 被呼叫端 test
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi   ; a 存於 rbp-4
mov DWORD PTR [rbp-8], esi   ; b 存於 rbp-8 (位址較低)
; 此時 &a > &b,呈現左到右

32 位元 cdecl 反組譯

; 呼叫端
push 2          ; 先壓第二參數
push 1          ; 再壓第一參數
call test
add esp, 8      ; 呼叫者清理

; 被呼叫端
push ebp
mov ebp, esp
; [ebp+8] = a (第一參數,位址較高)
; [ebp+12] = b (第二參數,位址較低)
; &a > &b,右到左入棧

棧框架通用佈局 (高位址 → 低位址)

[參數 n] ... [參數 2] [參數 1] [返回位址] [舊 EBP/RBP] [區域變數] ...

實務建議

  • **永遠不要依賴參數在棧上的相對位址** 撰寫可移植程式碼
  • 可變參數函數(printf 等)之所以要求右到左(cdecl),是為了讓固定參數位置固定,方便 va_start 定位
  • 64 位元 ABI 大量使用暫存器傳參,減少記憶體存取,效能顯著優於 32 位元
  • 內嵌組語或手寫序言/結語時,務必遵循目標平台 ABI 文件
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved