測試代碼結構
struct tagTest { int n1; long n2; DWORD n3; };
long funtest1(tagTest p1, int p2, LPCTSTR lpszP3); // cdecl (默認)
long __stdcall funtest2(tagTest p1, int p2, LPCTSTR lpszP3); // stdcall
1. cdecl 調用約定 (默認)
調用方匯編
; 參數從右向左入棧
push offset string_addr ; 第3參數 LPCTSTR
mov edx, [i2]; push edx ; 第2參數 int
sub esp, 0Ch ; 為結構體預留 12 字節
mov eax, esp
mov ecx, [tag1]; mov [eax], ecx ; 複製 n1
mov edx, [ebp-10h]; mov [eax+4], edx ; 複製 n2
mov ecx, [ebp-0Ch]; mov [eax+8], ecx ; 複製 n3
call funtest1
add esp, 14h ; 調用方清棧 (3參數 + 結構體 = 20 字節)
mov [ixx], eax ; 返回值在 eax
被調方匯編
funtest1:
push ebp
mov ebp, esp
push ecx ; 局部變量空間
mov dword ptr [p1], 3 ; p1.n1 = 3
mov eax, [lpszP3]; mov [lpszxx], eax
mov ecx, [p2]; mov [ebp+10h], ecx ; p1.n3 = p2
mov eax, 12Ch ; 返回值 300
mov esp, ebp
pop ebp
ret ; 只彈出返回地址,不清參數棧
2. stdcall 調用約定
調用方匯編
; 參數壓棧順序相同 (右→左)
push offset string_addr
mov eax, [i2]; push eax
sub esp, 0Ch; ... 複製結構體 ...
call funtest2
mov [ixx], eax
; 無 add esp 指令!被調方負責清棧
被調方匯編
funtest2:
push ebp
mov ebp, esp
push ecx
... 函數體同 cdecl ...
mov eax, 12Ch
mov esp, ebp
pop ebp
ret 14h ; 返回並清棧 20 字節 (0x14)
核心差異總結
| 特性 | cdecl | stdcall |
| 參數壓棧順序 | 右→左 | 右→左 |
| 棧清理責任 | 調用方 | 被調方 |
| 可變參數支持 | 支持 | 不支持 |
| 函數名修飾 | _FuncName | _FuncName@N |
| 典型用途 | C/C++ 默認 | Windows API、COM |
結構體參數傳遞細節
- 調用方用
sub esp, size 預留空間,再逐成員 mov 複製。
- 被調方直接以固定偏移訪問參數(
[ebp+8] 之後)。