函式呼叫的區別 cdecl以及 stdcall

2021-07-10 21:47:41 字數 2709 閱讀 4745

一、概念

1)_stdcall呼叫

_stdcall是pascal程式的預設呼叫方式,引數採用從右到左的壓棧方式,由呼叫者完成壓棧操作,被調函式自身在返回前清空堆疊。

win32 api都採用_stdcall呼叫方式,這樣的巨集定義說明了問題:  #define winapi _stdcall

按c編譯方式,_stdcall呼叫約定在輸出函式名前面加下劃線,後面加「@」符號和引數的位元組數,形如_functionname@number.

therefore, the function declared asint func( int a, double b )is decorated as follows:_func@12.

按c++編譯方式,可參看(三)

2)_cdecl呼叫

_cdecl是c/c++的預設呼叫方式,引數採用從右到左的壓棧方式,由呼叫者完成壓棧操作 ,傳送引數的記憶體棧由呼叫者維護。

_cedcl約定的函式只能被c/c++呼叫,每乙個呼叫它的函式都包含清空堆疊的**,所以產生的可執行檔案大小會比呼叫_stdcall函式的大。

按c編譯方式,_cdecl呼叫約定僅在輸出函式名前面加下劃線,形如_functionname。

按c++編譯方式,可參看(三)

二、區別

幾乎我們寫的每乙個windows api函式都是__stdcall型別的,首先,需要了解兩者之間的區別: windows的函式呼叫時需要用到棧(stack,一種先入後出的儲存結構)。當函式呼叫完成後,棧需要清除,這裡就是問題的關鍵,如何清除?如果我 們的函式使用了_cdecl,那麼棧的清除工作是由呼叫者,用com的術語來講就是客戶來完成的。這樣帶來了乙個棘手的問題,不同的編譯器產生棧的方式不 盡相同,那麼呼叫者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的問題就解決了,函式自己解決清除工作。所以,在跨(開發) 平台的呼叫中,我們都使用__stdcall(雖然有時是以winapi的樣子出現)。那麼為什麼還需要_cdecl呢?當我們遇到這樣的函式如 fprintf()它的引數是可變的,不定長的,被呼叫者事先無法知道引數的長度,事後的清除工作也無法正常的進行,因此,這種情況我們只能使用 _cdecl。到這裡我們有乙個結論,如果你的程式中沒有涉及可變引數,最好使用__stdcall關鍵字。

三、編譯時函式名修飾約定規則

1) 首先介紹一下函式修飾名,c」 或者「c++」函式在內部(編譯和鏈結)通過修飾名識別。修飾名是編譯器在編譯函式定義或者原型時生成的字串。有些情況下使用函式的修飾名是必要的,如 在模組定義檔案裡頭指定輸出「c++」過載函式、建構函式、析構函式,又如在彙編**裡呼叫「c」」或「c++」函式等。修飾名由函式名、類名、呼叫約 定、返回型別、引數等共同決定。名字修飾約定隨呼叫約定和編譯種類(c或c++)的不同而變化。函式名修飾約定隨編譯種類和呼叫約定的不同而不同。下面詳 細說明在c++編譯器中修飾名的約定方法,在c編譯器中的變化已經在(一)中闡述。

2)c++編譯時函式名修飾約定規則

__stdcall呼叫約定: 

1)、以"?"標識函式名的開始,後跟函式名; 

2)、函式名後面以"@@yg"標識參數列的開始,後跟參數列; 

3)、參數列以代號表示: 

x--void , 

d--char, 

e--unsigned char, 

f--short, 

h--int, 

i--unsigned int, 

j--long, 

k--unsigned long, 

m--float, 

n--double, 

_n--bool, 

pa--表示指標,後面的代號表明指標型別,如果相同型別的指標連續出現,以"0"代替,乙個"0"代表一次重複; 

4)、參數列的第一項為該函式的返回值型別,其後依次為引數的資料型別,指標標識在其所指資料型別前; 

5)、參數列後以"@z"標識整個名字的結束,如果該函式無引數,則以"z"標識結束。 

其格式為"?functionname@@yg*****@z"或"?functionname@@yg*xz",例如   int test1(char *var1,unsigned long)-----「?test1@@yghpadk@z」   void test2() -----「?test2@@ygxxz」 

__cdecl呼叫約定: 

規則同上面的_stdcall呼叫約定,只是參數列的開始標識由上面的"@@yg"變為"@@ya"。 

四、在c++編譯時,引入c庫裡面的函式的處理

如果add(int a, int b)是在c語言編譯器編譯,而在c++檔案使用,由(三)知,因為c編譯器和c++編譯器對函式名的解釋不一樣(c++編譯器解釋函式名的時候要考慮函式 引數,這樣是了方便函式過載,而在c語言中不存在函式過載的問題),會出現鏈結錯誤。 哪麼該如何處理呢?這是關鍵字extern就派上用場了。

而在c++檔案使用c編譯器編譯過的函式,則需要在c++檔案中宣告:extern "c" add(int a, int b),使用extern "c",實質就是告訴c++編譯器,該函式是c庫裡面的函式。請保持我的名稱,不要給我生成用於鏈結的函式修飾名。

另外順便說一下extern的另乙個用途,extern可以置於變數或者函式前,以標示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數和函式時在 其他模組中尋找其定義。#inlcude "***.***"     和 int   f()  等價於   extern int f() ;即 extern 申明的函式前不需要加引用標頭檔案就能用。

函式呼叫規範 cdecl和

cdecl stdcallc 和c 程式的預設呼叫規範 為了使用這種呼叫規範,需要你明確的加上 stdcall 或winapi 文字。即 return type stdcallfunction name argument list 在被呼叫函式 callee 返回後,由呼叫方 caller 調整堆疊...

函式呼叫規範 cdecl和

函式呼叫 規範 cdecl 和 stdcall的區別 一目了然 形式 原作 葡萄架上的牽牛花 cdecl stdcallc和 c 程式的預設呼叫規範 為了使用這種呼叫規範,需要你明確的加上 stdcall 或winapi 文字。即 return type stdcallfunction name a...

函式呼叫規範 cdecl和

原作 葡萄架上的牽牛花 cdecl stdcallc和 c 程式的預設呼叫規範 為了使用這種呼叫規範,需要你明確的加上 stdcall 或winapi 文字。即 return type stdcallfunction name argument list 在被呼叫函式 callee 返回後,由呼叫方...