C/C++ 預設 __cdecl,Win32 API/回呼函數用 __stdcall (WINAPI/CALLBACK)。兩者在組合語言層面有本質差異。本文以實際反組譯碼驗證參數壓棧順序、堆疊清理責任、名稱修飾規則。
void Input(int& m, int& n); // 參數為引用 (實際傳遞位址)
lea eax, [ebp-8] ; 取得 n 位址
push eax
lea ecx, [ebp-4] ; 取得 m 位址
push ecx
call Input ; 呼叫函數
add esp, 8 ; 【呼叫者清理堆疊】
add esp, 8 清理堆疊 (2 參數 × 4 位元組)。_ → _Input。Input:
push ebp
mov ebp, esp
; ... 函數本體 ...
pop ebp
ret ; 直接返回,無立即數
lea eax, [ebp-8]
push eax
lea ecx, [ebp-4]
push ecx
call Input ; 呼叫函數
; 【無 add esp】呼叫者不清理堆疊
Input:
push ebp
mov ebp, esp
; ... 函數本體 ...
pop ebp
ret 8 ; 【被呼叫者清理】ret 立即數 = 參數總位元組數
ret N 清理堆疊。_Func@N (N = 參數總位元組數,十進位)。__cdecl:[ebp-8] 存 n 位址,[ebp-4] 存 m 位址 (引用傳遞即傳位址)。__stdcall:函數內 [ebp+8] 為第一參數 (m 位址),[ebp+12] 為第二參數 (n 位址),與壓棧順序一致。| 慣例 | 關鍵字 | 參數傳遞 | 堆疊清理 | 名稱修飾 | 典型用途 |
|---|---|---|---|---|---|
| __cdecl | 預設 | 右至左壓棧 | 呼叫者 | _Func | C/C++ 一般函數、可變參數 |
| __stdcall | WINAPI, CALLBACK, PASCAL | 右至左壓棧 | 被呼叫者 | _Func@N | Win32 API、回呼、COM |
| __fastcall | __fastcall | 前兩參數 ECX/EDX,其餘右至左 | 被呼叫者 | @Func@N | 高效能需求 |
| thiscall | 隱含 (成員函數) | this 在 ECX,參數右至左 | 被呼叫者 | 類別內部 | 非靜態成員函數 |
| naked call | __declspec(naked) | 完全自定 | 自定 | 無 | 手寫組語進出棧 |
__cdecl 或 __stdcall 避免不匹配。WINAPI 宏定義為 __stdcall,自訂 API 應使用此宏保持一致性。/Gd=cdecl, /Gz=stdcall, /Gr=fastcall)。呼叫慣例是 C/C++ 與作業系統、DLL、組語互操作的基石。透過反組譯觀察 push/call/add esp/ret N 細節,能徹底理解參數傳遞與堆疊管理機制,避開跨模組呼叫的隱形陷阱。