在 C/C++ 程式設計中,函數調用約定決定了參數如何傳遞、堆疊由誰清理以及函數名稱如何修飾。理解這些約定對於混合語言編程、DLL 導出、以及底層除錯至關重要。本文將系統介紹五種常見的調用約定:stdcall、cdecl、fastcall、thiscall 以及較少見的 naked call。
函數調用時,電腦通常使用堆疊來存儲傳遞給函數的參數。堆疊是一種「後進先出」的資料結構,擁有存儲區與堆疊頂指標。參數壓入堆疊的順序、以及函數返回後由呼叫者還是被呼叫者負責清理堆疊,構成了調用約定的核心差異。
語法: int _stdcall function(int a, int b);
@ 與參數總位元組數(如 _function@8)組合語言示例:
push b ; 先壓入第二個參數
push a ; 再壓入第一個參數
call function
; 無需呼叫者清理堆疊
語法: int function(int a, int b); 或 int _cdecl function(int a, int b);
_function)因為由呼叫者清理堆疊,cdecl 允許函數參數個數不固定(如 printf),這是 C 語言的一大特性。
push b
push a
call function
add esp, 8 ; 呼叫者清理堆疊
語法: int _fastcall function(int a, int b);
ECX、EDX 寄存器傳遞@function@8)thiscall 無法顯式指定,專用於 C++ 類成員函數。因為成員函數隱含 this 指標,故需特殊處理:
this 通過 ECX 傳遞;若不固定,this 在所有參數壓入後壓入堆疊極少見的調用約定,高級語言中罕用。編譯器不產生序言/結語代碼,完全由程式設計師以組合語言控制堆疊幀,僅用於極度優化或特殊系統級程式碼。
__stdcall:_function@參數位元組數__cdecl:_function__fastcall:@function@參數位元組數C++ 為支援多載,編碼更複雜,以 ? 開頭,後接函數名、@@、呼叫約定標識(YG=stdcall, YA=cdecl, YI=fastcall)、參數類型編碼、以 @Z 結尾。例如:
int Test(char*, unsigned long) → ?Test@@YGHPADK@Z
| 約定 | 參數順序 | 堆疊清理 | 典型用途 |
|---|---|---|---|
| stdcall | 右→左 | 被呼叫者 | Win32 API、COM |
| cdecl | 右→左 | 呼叫者 | C/C++ 預設、可變參數 |
| fastcall | 前兩個用寄存器 | 被呼叫者 | 效能關鍵路徑 |
| thiscall | 右→左 | 視參數固定性 | C++ 非靜態成員函數 |
聲明與實作的調用約定必須一致,否則會導致堆疊損壞、程式崩潰或難以診斷的錯誤。跨模組(DLL/EXE)介面建議統一使用 stdcall 或 cdecl 並顯式標註。