最近在看《windows程序設計》一書,在書中看到使用windows函數的時候,要在函數前面加上WINAPI這樣一個關鍵字。WINAPI 是在WINDEF.H中定義的,其定義如下
#define WINAPI __stdcall
其中__stdcall是一種函數調用的約定。
首先,這裏要清楚的第一個問題是:什麼是函數調用的約定?
通過在網上搜索,好搜百科給出瞭如下的定義:
函數調用約定,是指當一個函數被調用時,函數的參數會傳遞給被調用的函數和返回值會被返回給調用函數。函數的調用約定就是描述參數是怎麼傳遞和由誰平衡堆棧的,當然還有返回值。
其中常用的函數約定有三種類型,分別是:
__pascal,
__stdcall,
__cdecl,
他們之間的區別如下表所示:
參數傳遞順序 | 誰負責清理參數佔用的堆棧 | |
__pascal | 從左到有 | 調用者 |
__stdcall | 從右到左 | 被調函數 |
__cdecl | 從右到左 | 調用者 |
調用函數的代碼和被調用函數必須採用相同的函數調用約定,程序才能正常運行。在Windows上,__cdecl是c/c++程序的缺省函數調用約定。
在有的CPU上,編譯器會用寄存器傳遞參數,函數使用的堆棧由被調用函數分配和釋放。這種調用約定在行爲上和__cdecl有一個共同點:實參和形參數目不符不會導致堆棧錯誤。
不過,即使用寄存器傳遞參數,編譯器在進入函數時,還是會將寄存器裏的參數存入堆棧指定的位置。參數和局部變量一樣應該在堆棧中有一席之地。參數可以被理解爲由調用函數指定的局部變量。
其中,VC默認使用__cdecl。所以如果需要使用__stdcall,可採用兩種方法:
(1)可以在函數名前手工添加,只對單一函數有效
(2)直接修改工程屬性(C/C++ > Advanced > Calling Convention)來一次性配置所有的函數
__cdecl可實現變長參數列表
__stdcall產生的代碼更小
__cdecl的運行速度更快,這和內聯函數有點類似,代碼越多當然運行的越快
__cdecl主調用函數進行參數壓棧並且恢復堆棧
__stdcall主調用函數進行參數壓棧,被調函數恢復堆棧
所以如果使用__cdecl的函數多次調用同一函數,就產生多分恢復碼
一份恢復碼只能將一種長度的參數表出棧,所以要對不同長度的參數表堆棧恢復,必須要有多分恢復碼,所以變長參數列表必須由主調函數恢復堆棧
__stdcall調用約定在輸出函數名前加上一個下劃線前綴,後面加上一個'@'符號和其參數的字節數,格式爲:_functionname@number
__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式爲:_functionname
__stdcall通常用於DLL的創建(以支持多語言的調用),此外Win32API函數皆用__stdcall,所以Win32程序中的自定義函數也最好使用__stdcall
__cdecl非DLL的Console程序