函數調用約定

函數調用約定

常見的函數調用約定[5]:cdecl,stdcall,fastcall,thiscall,naked call
MFC調用約定(VS6:Project Settings->C/C++ <Category:Code Generation> Calling convention:)c++

1. __cdecl(C調用約定.The C default calling convention)C/C++ 缺省調用方式

a.壓棧順序:函數參數從右到左.
b.參數棧維護:由調用函數把參數彈出棧,傳送參數的內存棧由調用函數來維護
(正由於如此,實現可變參數vararg的函數(如printf)只能使用該調用約定).
c.函數修飾名約定:VC將函數編譯後會在函數名前面加上下劃線前綴
d.每個調用它的函數都包含清空堆棧的代碼,因此產生的可執行文件大小會比調用_stdcall函數的大程序員

2.__stdcall (Pascal方式清理C方式壓棧,一般用於Win32 Api中)

a.壓棧順序:函數參數從右到左的壓棧順序。ide

b.參數棧維護:被調用函數把參數彈出棧(在退出時清空堆棧)。函數

c.函數修飾名約定:VC將函數編譯後會在函數名前面加上下劃線前綴,在函數名後加上"@"和參數的字節數。
ex. VC: int f(void *p) (編譯後)-> _f@4(在外部彙編語言裏能夠用這個名字引用這個函數)優化

3.__fastcall (快速調用約定,經過寄存器來傳送參數)

a.壓棧順序:用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的 參數仍舊自右向左壓棧傳送
b.參數棧維護:被調用函數在返回前清理傳送參數的內存棧
c.函數修飾名約定:VC將函數編譯後會在函數名前面加上"@"前綴,在函數名後加上"@"和參數的字節數ui

4. thiscall (自己調用,僅用於「C++」成員函數)

a.壓棧順序:this指針存放於CX/ECX寄存器中,參數從右到左的壓棧順序
b.thiscall不是關鍵詞,所以不能被程序員指定this

5. naked call (裸調)

a..當採用1-4的調用約定時,若是必要的話,進入函數時編譯器會產生代碼來
保存ESI,EDI,EBX,EBP寄存器,退出函數時則產生代碼恢復這些寄存器的內容
(這些代碼稱做 prolog and epilog code,通常,ebp,esp的保存是必須的)
b.naked call不產生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用.net

關鍵字 _stdcall、_cdecl和_fastcall(1-3)能夠直接加在要輸出的函數前。
它們對應的命令行參數分別爲/Gz、/Gd和/Gr。缺省狀態爲/Gd,即__cdecl
要徹底模仿PASCAL調用約定首先必須使用_stdcall調用約定,函數名修飾約定可經過其它方法模仿
WINAPI宏,Windows.h支持該宏,它能夠將出函數翻譯成適當的調用約定,
在WIN32中,它被定義爲_stdcall。使用WINAPI宏能夠建立本身的APIs命令行

引用:

不少API函數就是象這樣聲明的:
int WINAPI MessageBoxA(HWND,LPCSTR,LPSTR,UINT);
而WINAPI實際上就是_stdcall.
大多數API都採用_stdcall調用規範,這是由於幾乎全部的語言都支持_stdcall調用.
相比之下,_cdecl只有在C語言中才能用. 但_cdecl調用有一個特色,就是可以實現可變參數的函數調用,
好比printf,這用_stdcall調用是不可能的.
_fastcall這種調用規範比較少見,可是在Borland C++ Builder中比較多的採用了這種調用方式.
若是有共享代碼的須要,好比寫DLL,推薦的方法是用_stdcall調用,由於這樣適用範圍最廣.
若是是C++語言寫的代碼供Delphi這樣的語言調用就必須聲明爲_stdcall,
由於Pascal不支持cdecl調用(或許Delphi的最新版本可以支持也說不定,這個不太清楚).
在其餘一些地方,好比寫COM組件,幾乎都用的是stdcall調用.
在VC或Delphi或C++Builder裏面均可以從項目設置中更改默認的函數調用規範,
固然也可在函數聲明時加入_stdcall,_cdecl,_fastcall關鍵字來明確的指示本函數用哪一種調用約定.
__declspec通常都是用來聲明DLL中的導出函數.這個關鍵字也有一些其餘的用法,不過很是罕見.
_declspec主要是用於說明DLL的引出函數的,在某些狀況下用_declspec(dllexport)在DLL中聲明引出函數,
比用傳統的DEF文件方便.在普通程序中也可用_declspec(dllimport)說明函數是位於另外一個DLL中的導出函數.
如下是在dev-c++裏創建自已的dll時的dll.h裏面的代碼,這裏面有一個:_declspec(dllexport)翻譯

#ifndef _DLLH
#define _DLLH//防重複定義

#if BUILDING_DLL
#define DLLIMPORT declspec (dllexport)
#else
#define DLLIMPORT
declspec (dllimport)
#endif
DLLIMPORT void HelloWorld (void);
#endif
上面代碼裏面的_delcspce(dllexport)被定義爲宏,這樣能夠提升程序的可讀性.

這個的做用是將函數定義爲導出函數,也就是說這個函數要被包含這個函數的程序以外的程序調用.

本語句中就是:void Helloword(void):

摘自msdn:在 32 位編譯器版本中,可使用 __declspec(dllexport) 關鍵字從 DLL 導出數據、

函數、類或類成員函數。__declspec(dllexport) 將導出指令添加到對象文件

若要導出函數,__declspec(dllexport) 關鍵字必須出如今調用約定關鍵字的左邊(若是指定了關鍵字)

例如:

_declspec(dllexport) void __cdecl Function1(void);

若要導出類中的全部公共數據成員和成員函數,關鍵字必須出如今類名的左邊,以下所示:

class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };

生成 DLL 時,一般建立一個包含正在導出的函數原型和/或類的頭文件,並將 __declspec(dllexport)

添加到頭文件中的聲明。若要提升代碼的可讀性,請爲 __declspec(dllexport) 定義一個宏並對正在導出的

每一個符號使用該宏:#define DllExport __declspec( dllexport )

__declspec(dllexport) 將函數名存儲在 DLL 的導出表中。若是但願優化表的大小

附錄>

1. 修飾名(Decoration name)

  「C」或者「C++」函數在內部(編譯和連接)經過修飾名識別。修飾名是編譯器在編譯函數定義或者原

  型時生成的字符串。有些狀況下使用函數的修飾名是必要的,如在模塊定義文件裏頭指定輸出「C++」

  重載函數、構造函數、析構函數,又如在彙編代碼裏調用「C」」或「C++」函數等。

  1>修飾名由函數名、類名、調用約定、返回類型、參數等共同決定。

  2>名字修飾約定隨調用約定和編譯種類(C或C++)的不一樣而變化。

  函數名修飾約定隨編譯種類和調用約定的不一樣而不一樣,下面分別說明。

  •  a、C編譯時函數名修飾約定規則:

  __stdcall調用約定在輸出函數名前加上一個下劃線前綴,後面加上一個「@」符號和其參數的字節數,

  格式爲_functionname@number。

  __cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式爲_functionname。

  __fastcall調用約定在輸出函數名前加上一個「@」符號,後面也是一個「@」符號和其參數的字節數,

  格式爲@functionname@number。

  它們均不改變輸出函數名中的字符大小寫,這和PASCAL調用約定不一樣,

PASCAL約定輸出的函數名無任何修飾且所有大寫。

  •   b、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@@YGXZ」,例如

  int Test1(char *var1,unsigned long)-----「?Test1@@YGHPADK@Z」

  void Test2() -----「?Test2@@YGXXZ」

  __cdecl調用約定:

  規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的「@@YG」變爲「@@YA」。

  __fastcall調用約定:

  規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的「@@YG」變爲「@@YI」。

  VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用

轉自:https://blog.csdn.net/lioncolumn/article/details/10376891
相關文章
相關標籤/搜索