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

C/C++ 呼叫慣例與可變參數表深度解析

     編輯:關於VC++

前言

呼叫慣例定義了函式呼叫時參數傳遞順序、堆疊清理責任與返回值處理方式。Microsoft Visual C++ 支援五種慣例:__cdecl、__stdcall、__fastcall、thiscall 與 naked。所有慣例均以 EAX 傳回 32 位元值,64 位元值則用 EDX:EAX 配對。naked 極具彈性,此處不贅述。以下聚焦前四種核心慣例。

四大呼叫慣例對照表

關鍵字堆疊清理者參數壓入順序
__cdecl呼叫者從右至左
__stdcall被呼叫者從右至左
__fastcall被呼叫者從右至左,前兩個參數分別置於 ECX、EDX
thiscall視可變參數而定從右至左

各慣例特性與適用場景

__cdecl(C/C++ 預設)

  • 優點:呼叫者清理堆疊,天然支援可變參數(如 printf)。
  • 缺點:每次呼叫需額外執行 ADD ESP, n 指令,增加程式碼體積。

__stdcall(Win32 API 標準)

  • 所有固定參數的 Windows API 均採用此慣例。
  • 被呼叫者在 RET n 指令中自動清理堆疊,呼叫端無額外指令。

__fastcall(核心層高效呼叫)

  • 前兩個參數透過暖存器 ECX、EDX 傳遞,減少記憶體存取,效率最高。
  • 廣泛用於 Windows 核心元件內部。

thiscall(C++ 成員函式預設)

  • 非可變參數成員函式:this 指標置於 ECX,參數入堆疊,被呼叫者清理。
  • 可變參數成員函式:退化為 __cdecl 風格,this 最後壓入堆疊,呼叫者清理。

慣例不匹配導致的經典崩潰案例

DWORD ThreadFunc(LPVOID lpParam) { /* ... */ }
CreateThread(..., (LPTHREAD_START_ROUTINE)ThreadFunc, ...);

若編譯選項未指定 /Gz(強制 __stdcall),編譯器預設產生 __cdecl 版本的 ThreadFunc。CreateThread 期望 __stdcall,執行緒啟動時可能正常,但結束時因堆疊未正確清理(呼叫者以為被呼叫者會清理,實則未清理),導致存取違規或非法指令錯誤。

修正:在函式定義前加上 __stdcall 或專案設定 /Gz。

堆疊結構與清理機制圖解

執行緒堆疊運行狀態

圖 1:執行緒堆疊運行狀態示意

呼叫前後 ESP 差值區間包含參數表與返回位址。以 int __cdecl func(void* p) 為例,呼叫端組合語言:

PUSH p
CALL func
ADD ESP, 4    ; __cdecl 呼叫者清理

若為 __stdcall,則無 ADD ESP, 4,func 內部以 RET 4 清理。

可變參數表實現原理

唯有 __cdecl 支援可變參數,因呼叫者知曉參數個數與大小,方能正確清理堆疊。CRT 於 varargs.h 定義核心巨集:

typedef char *va_list;
#define _INTSIZEOF(n)  ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define va_start(ap)   (ap = (va_list)&va_alist)
#define va_arg(ap, t)  (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define va_end(ap)     (ap = (va_list)0)
  • va_list 實為 char*,大小等於 CPU 位址寬度(32 位元為 4 位元組)。
  • _INTSIZEOF 依型別大小對齊至 int 倍數。
  • va_start 取得最後一個固定參數在堆疊上的位址。
  • va_arg 先移動指標至下一參數,再解參考取值。
  • va_end 歸零指標,結束取參流程。

本質上即:定位堆疊起始位置 → 依型別大小逐個取出參數值。

結語

理解呼叫慣例與堆疊操作,是避免跨模組、跨語言、多執行緒介面錯誤的關鍵。結合實例與 CRT 原始碼分析,可見可變參數表實際上就是對堆疊記憶體的有序走訪與解讀。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved