Calling Conventions (呼叫慣例)
因為不同的語言間有不同的傳遞參數的方法,而C/C++編譯器為了能夠使用由其他語言開發出來的函式庫加上了這些參數傳遞方式不同的方式就稱為呼叫慣例,而在C++Builder比較長看到的是__fastcall
__stdcall是為了兼容Windows,__stdcall呼叫慣例相當於16位元動態函式庫中經常使用的PASCAL呼叫慣例,在Windows中, WINAPI就是一個定義為__stdcall的巨集,Windows.h支援此巨集,它可以將輸出函數翻譯成適當的呼叫慣例,VB、VFP等也採用這個慣例。
函數名修飾約定也有不同,對於C編譯器,__stdcall呼叫慣例在輸出函數名前加上一個底線的前綴,後面加上一個「@」符號和其參數的byte數字,格式為 _functionname@number。
當你LoadLibrary一個DLL文件後,把GetProcAddress取得的函數地址傳給上面三個執行緒生成函數時,請務必確認實際定義在DLL文件的輸出函數符合呼叫慣例要求。否則,編譯成 Release版後執行,可能會破壞堆疊,而且程式的行為會變的不可預測。
定義四個函式分別使用__fastcall 、__stdcall 與__cdecl 與不指定四種呼叫慣例:
#define DLLEXP extern “C” __declspec(dllexport)
DLLEXP int MyFunc_Default(char *c,int X)
{
return X;
}
DLLEXP int __fastcall MyFunc_Fast(char *c,int X)
{
return X;
}
DLLEXP int __stdcall MyFunc_Std(char *c,int X)
{
return X;
}
DLLEXP int __cdecl MyFunc_Cdecl(char *c,int X)
{
return X;
}
編譯完成後使用tdump與impdef來觀察這些函式的輸出結果| 呼 叫 慣 例 | 原 始 函 式 | Borland C++Builder | Microsoft Visual C++ |
| __cdecl | MyFunc_cdcel | _MyFunc_cdcel | MyFunc_cdcel |
| __stdcall | MyFunc_std | MyFunc_std | _MyFunc_std@8 |
| __fastcall | MyFunc_fast | @MyFunc_fast | @MyFunc_fast@8 |
| ─ | MyFunc_default | _MyFunc_default | MyFunc_default |
| 預 設 呼 叫 慣 例 | ─ | __cdecl | __cdecl |
__cdecl 之前說 由呼叫者來調整堆疊
push arg3
push arg2
push arg1
call foo
add sp, 12__stdcall 之前說 由被呼叫者調整堆疊,這樣的做法可以節省程式的大小
//函式 foo(int arg1, int arg2, int arg3)
push arg3
push arg2
push arg1
call foo
//stack 的清除工作會在 foo 函式內完成 The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code. The following list shows the implementation of this calling convention.
__fastcall 會使用暫存器來傳遞參數 (如 ecx, edx) 以加速程式的運作
1. //函式 foo(int arg1, int arg2, int arg3)
2. push arg3
3. mov edx, arg2
4. mov ecx, arg1
5. call foo 通常__cdecl 都會被當成預設的,可以省略它,除非我們用了/Gz (stdcall) 或 /Gr (fastcall) ,/Gd就是強制給__cdecl
Linker 參數
__cdecl : cl /Gd
__stdcall : cl /Gz
__fastcall : cl / Gr
以上轉自維尼的蜂巢

0 意見:
張貼留言