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

C++ 函數調用約定深度解析:x86/x64 棧幀、參數傳遞、調試與跨語言互操作

     編輯:C++ 入門

調用約定核心要素

函數調用約定規範:

  1. 參數傳遞順序與位置 (棧/寄存器)。
  2. 棧平衡責任方 (調用者/被調用者)。
  3. 返回值傳遞方式 (寄存器/棧/隱藏指針)。
  4. 名稱修飾規則 (符號表呈現形式)。
  5. 被調用者保存寄存器 (callee-saved: RBX, RBP, RDI, RSI, R12-R15)。

主流調用約定對比表

約定架構參數位置棧清理名稱修飾 (MSVC)用途
cdeclx86棧 (右→左)調用者_funcC/C++ 默認、可變參數
stdcallx86棧 (右→左)被調用者_func@NWin32 API、COM
fastcallx86前 2 參 ECX/EDX,其餘棧被調用者@func@N舊版優化
thiscallx86this 在 ECX,其餘棧被調用者類內部編碼C++ 非靜態成員函數
vectorcallx86/x64前 4 整數/6 浮點寄存器被調用者func@@Y...ZSIMD 密集、數學庫
Microsoft x64x64前 4: RCX,RDX,R8,R9 (整數/指針)
XMM0-XMM3 (浮點)
調用者
(影子空間 32B)
無前綴
PE/COFF 導出名
Windows x64 唯一約定
System V AMD64 ABIx64前 6: RDI,RSI,RDX,RCX,R8,R9
XMM0-XMM7
調用者
(紅區 128B)
無前綴
ELF 導出名
Linux/macOS/BSD x64

x64 Microsoft 呼叫約定實戰剖析

參數傳遞示例


// 原型
void func(int a, double b, char* c, float d, long long e, int f);

// 調用點彙編 (MSVC 輸出)
mov     rcx, 1              ; a (整數 1)
movsd   xmm0, 2.5           ; b (浮點 2)
lea     rdx, str            ; c (指釥 3)
movss   xmm1, 3.14          ; d (浮點 4)
mov     r8, 100             ; e (整數 5)
mov     r9, 200             ; f (整數 6)
call    func
; 調用者負責分配 32 字節影子空間 (home space)
; 被調用者可選擇將寄存器參數存入影子空間

棧幀佈局 (典型)


func PROC
    ; 序言
    push    rbp
    mov     rbp, rsp
    sub     rsp, 48h        ; 局部變量 + 對齊 16B
    ; 可選:保存寄存器參數到影子空間
    mov     [rbp+10h], rcx  ; a
    movsd   [rbp+18h], xmm0 ; b
    mov     [rbp+20h], rdx  ; c
    movss   [rbp+28h], xmm1 ; d
    mov     [rbp+30h], r8   ; e
    mov     [rbp+38h], r9   ; f
    ; 函數體...
    ; 尾聲
    add     rsp, 48h
    pop     rbp
    ret
func ENDP

影子空間 / Home Space 關鍵點

  • 調用者必須分配 32 字節 (4×8B),即使參數 < 4 個。
  • 被調用者可選使用 存儲寄存器參數 (便於調試/可變參數 va_start)。
  • 調用鏈中每層函數入口需保證 RSP 16 字節對齊 (調用 call 前 RSP % 16 == 8)。

返回值傳遞

調試與診斷技巧

  1. 查看調用棧參數:VS 調試器「調試 → 窗口 → 寄存器」+「反彙編」,手動解析 RCX/RDX/R8/R9/XMM0-3。
  2. 檢查棧平衡:/RTCs (運行時檢查) 捕獲調用約定不匹配導致的棧損壞。
  3. 強制調用約定:__cdecl, __stdcall, __vectorcall, __fastcall (x86 僅)。
  4. 跨語言互操作:

// C++ DLL 導出給 C#/Python/Rust
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b); // 固定 stdcall

// C# P/Invoke
[DllImport("mydll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int Add(int a, int b);

現代最佳實踐

  • x64 無需關心調用約定:平台僅有一個約定,編譯器自動處理。
  • 性能熱路徑:__vectorcall (MSVC) 或屬性 [[gnu::vectorcall]] (GCC/Clang) 顯式啟用 SIMD 寄存器傳遞。
  • 避免可變參數:printf 風格破壞類型安全,改用 std::format / {fmt} / 變參模板。
  • 內聯優先:inline/__forceinline 消除調用開銷,讓優化器直接處理數據流。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型大小傳遞方式示例
≤ 8 字節 (整數/指針)RAXint, void*, std::unique_ptr
≤ 8 字節 (浮點)XMM0float, double
9-16 字節RAX:RDX (整數) / XMM0:XMM1 (浮點)std::pair
> 16 字節 或 非平凡拷貝調用者分配內存,傳遞隱藏指針作第 1 參數 (RCX)std::vector, std::string (SSO 除外)