函數調用約定

最近在看《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程序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章