一、概念
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 返回後,由呼叫方...