DWORD WINAPI?stdcall?


網絡編程實驗2_(4)基於流式套接字的服務器程序設計中,創建了以下這個函數:

DWORD WINAPI ClientThread(LPVOID lpParameter)

以前我只學過類似:

int swap(int x,int y)

這樣的函數,而這個函數看起來就很詭異,如何理解這個函數呢?


 

DWORD是數據類型,在這里是返回值,返回32位數據。

在visual studio中WINAPI轉到定義可以看到:

#define WINAPI __stdcall 

也就是說WINAPI是一個宏,所代表的符號是__stdcall。 

WINAPI是函數調用形式,windows API函數采用__stdcall標准調用約定,即由被調用函數來清理棧中的參數,這種方式是不能實現可變參數的。 

__stdcall是函數調用約定的一種,函數調用約定主要約束了兩件事:

1.參數傳遞順序

2.調用堆棧由誰(調用函數或被調用函數)清理

常見的函數調用約定:stdcall cdecl fastcall thiscall naked call

__stdcall表示

1.參數從右向左壓入堆棧

2.函數被調用者修改堆棧

3.函數名(在編譯器這個層次)自動加前導的下划線,后面緊跟一個@符號,其后緊跟着參數的尺寸

在win32應用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常見。


摘自:

__stdcall詳解

在C語言中,假設我們有這樣的一個函數:int function(int a,int b)
調用時只要用result = function(1,2)這樣的方式就可以使用這個函數。但是,當高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函數調用需要多少個、什么樣的參數,也沒有硬件可以保存這些參數。也就是說,計算機不知道怎么給這個函數傳遞參數,傳遞參數的工作必須由函數調用者和函數本身來協調。為此,計算機提供了一種被稱為棧的數據結構來支持參數傳遞。
棧是一種先進后出的數據結構,棧有一個存儲區、一個棧頂指針。棧頂指針指向堆棧中第一個可用的數據項(被稱為棧頂)。用戶可以在棧頂上方向棧中加入數據,這個操作被稱為壓棧(Push),壓棧以后,棧頂自動變成新加入數據項的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧后,棧頂下的一個元素變成棧頂,棧頂指針隨之修改。函數調用時,調用者依次把參數壓棧,然后調用函數,函數被調用以后,在堆棧中取得數據,並進行計算。函數計算結束以后,或者調用者、或者函數本身修改棧,使堆棧恢復原裝。
在參數傳遞中,有兩個很重要的問題必須得到明確說明:
當參數個數多於一個時,按照什么順序把參數壓入堆棧函數調用后,由誰來把堆棧恢復原裝。在高級語言中,通過函數調用約定來說明這兩個問題。常見的調用約定有:
stdcall,cdecl,fastcall,thiscall,naked call


 

為了深刻理解__stdcall,我查找了一些資料,參考鏈接如下:

C++知識回顧之__stdcall、__cdcel和__fastcall三者的區別

(這位老哥講的很全面,但是格式有點亂。於是我稍微調整了一下格式)

__stdcall、__cdecl和__fastcall是三種函數調用協議,函數調用協議會影響函數參數的入棧方式、棧內數據的清除方式、編譯器函數名的修飾規則等。

 

調用協議常用場合

__stdcall:Windows API默認的函數調用協議。

__cdecl:C/C++默認的函數調用協議。

__fastcall:適用於對性能要求較高的場合

 

函數參數入棧方式

__stdcall:函數參數由右向左入棧。

__cdecl:函數參數由右向左入棧。

__fastcall:從左開始不大於4字節的參數放入CPU的ECX和EDX寄存器,其余參數從右向左入棧。

 

棧內數據清除方式

__stdcall:函數調用結束后由被調用函數清除棧內數據。

__cdecl:函數調用結束后由函數調用者清除棧內數據。

__fastcall:函數調用結束后由被調用函數清除棧內數據。

(1)不同編譯器設定的棧結構不盡相同,跨開發平台時由函數調用者清除棧內數據不可行。

(2)某些函數的參數是可變的,如printf函數,這樣的函數只能由函數調用者清除棧內數據。

(3)由調用者清除棧內數據時,每次調用都包含清除棧內數據的代碼,故可執行文件較大。

 

C語言編譯器函數名稱修飾規則

__stdcall:編譯后,函數名被修飾為“_functionname@number”。

__cdecl:編譯后,函數名被修飾為“_functionname”。

__fastcall:編譯后,函數名給修飾為“@functionname@nmuber”。

注:“functionname”為函數名,“number”為參數字節數。

注:函數實現和函數定義時如果使用了不同的函數調用協議,則無法實現函數調用。

 

C++語言編譯器函數名稱修飾規則

__stdcall:編譯后,函數名被修飾為“?functionname@@YG******@Z”。

__cdecl:編譯后,函數名被修飾為“?functionname@@YA******@Z”。

__fastcall:編譯后,函數名被修飾為“?functionname@@YI******@Z”

 

注:“******”為函數返回值類型和參數類型表。

注:函數實現和函數定義時如果使用了不同的函數調用協議,則無法實現函數調用。

C語言和C++語言間如果不進行特殊處理,也無法實現函數的互相調用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM