調用約定規範:參數壓棧順序、棧平衡責任方、名字修飾規則。不匹配將導致棧破壞、鏈接錯誤或運行時崩潰。
| 約定 | 參數順序 | 棧清理 | 可變參 | 名字修飾 | 典型場景 |
|---|---|---|---|---|---|
| cdecl | 右→左 | 調用者 | ✓ | _func | C/C++ 默認 |
| stdcall | 右→左 | 被調用者 | ✗ | _func@N | Win32 API |
| fastcall | 前兩參寄存器(ECX/EDX),其餘右→左 | 被調用者 | ✗ | @func@N | 高性能調用 |
| thiscall | 右→左,this 在 ECX | 被調用者(固定參)/調用者(可變參) | 視情況 | C++ 專用 | C++ 非靜態成員函數 |
| naked | 自定義 | 自定義 | 自定義 | 無 | 內嵌彙編/裸機 |
若左→右壓棧,首參數偏移量需知參數總數,可變參無法定位;若被調用者清棧,無法知曉實際參數大小。
stdcall(被調用者清棧,調用方無需關心參數大小)。extern "C" 禁用 C++ 名字修飾。__attribute__((stdcall));MSVC:__stdcall。tail(1, 2, 3, (void*)0);
// 棧佈局(高地址→低地址)
[EBP+8] = 1
[EBP+12] = 2
[EBP+16] = 3
[EBP+20] = 0
tail(1.414, 2, 3.998e10);
// [EBP+8] = 1.414 低 4 字節
// [EBP+12] = 1.414 高 4 字節
// [EBP+16] = 2
// [EBP+20] = 3.998e10 低
// [EBP+24] = 3.998e10 高
| 類型大小 | 返回方式 |
|---|---|
| ≤ 4 字節 (int, ptr, float) | EAX |
| 4~8 字節 (long long, double) | EDX:EAX (高:低) |
| 浮點 (float/double) | x87 FPU 棧頂 ST(0) |
| 結構體/聯合體 | 隱式傳入返回地址指針作首參數 (foo(&ret, ...)) |
int* bad() { int x = 10; return &x; } // 錯誤!函數返回後棧幀失效
int* good() { static int x = 10; return &x; } // 正確:靜態存儲區
int* also_good(int *out) { *out = 10; return out; } // 正確:調用者提供存儲