首頁 > 軟體

C和C++的函數呼叫約定你知道多少

2022-03-03 19:01:53

呼叫方式

C/C++函數有多種呼叫約定。

C語言:

  • __cdecl
  • __stdcall
  • __fastcall
  • naked
  • __pascal

C++比C語言多了一種:

__thiscall

1. __cdecl

__cdecl呼叫約定又稱為C呼叫約定,時C/C++語言預設的呼叫約定。引數按照從右向左的方式入棧,函數本身不清理棧,此工作由呼叫者負責,返回值在EAX中。
由於由呼叫者清理站,所以允許可變參函數存在。

圖示:

int __cdecl add(int a, int b)
{
	int c = a + b;
	return c;
}
int main(void)
{
	int x = 1, y = 2;
	int z = add(x, y);
	return 0;
}

函數本身不清理棧,此工作由呼叫者負責

2. __stdcall

引數按照從右至左的方式入棧,函數自身清理堆疊,返回值在EAX中。

範例:

int __cdecl add(int a, int b)
{
	int c = a + b;
	return c;
}
int main(void)
{
	int x = 1, y = 2;
	int z = add(x, y);
	return 0;
}


3. __fastcall

顧名思義,__fastcall 的特點就是快,因為它通過CPU暫存器來傳遞引數。他用ECX和EDX傳送前兩個雙字(DWORD)或更小的引數,剩下的引數按照從右至左的方式入棧,函數自身清理堆疊,返回值在EAX中。

4. naked

naked是一個很少見的呼叫約定,一般不建議使用。編譯器不會給這種函數增加初始化和清理程式碼,更特殊的是你不能用return返回返回值,只能用插入組合返回結果,此呼叫約定必須跟_declspec同時使用。例如定義一個求和程式,如:_declspec (naked) int add(int a,int b) ;

5. __pascal

這是pascal語言的呼叫約定,跟_stdcall一樣,引數按照從右至左的方式入棧,函數自身清理堆疊,返回值在EAX中。VS 中已經廢棄了這種呼叫方式,因此在寫VS程式時,建議使用_stdcall 代替。

6. __thiscall

這是C++語言特有的一種呼叫方式,用於類成員函數的呼叫約定。如果引數確定,this 指標存放於ECX 暫存器,函數自身清理堆疊;如果引數不確定,this 指標在所有引數入棧後再入棧,呼叫者清理棧。_thiscall 不是關鍵字,程式設計師不能使用。引數按照從右至左的方式入棧。

範例:

class Object
{
private:
	int value;
	int nums;
public:
	Object(int x = 0, int y = 0)
	{
		value = x;
		nums = y;
	}
	~Object() {}
	void Print()const
	{
		cout << value << endl;
	}
};
int main(void)
{
	Object obj(10, 20);
	obj.Print();
	return 0;
}

函數自身清理堆疊

物件地址放入暫存器 ECX中

名字修飾約定

1.修飾名

“C”或者“C++”函數在內部(編譯和連結)通過修飾名識別。修飾名是編譯器在編譯函數定義或者原型時生成的字串。有些情況下使用函數的修飾名是必要的,如在模組定義檔案裡頭指定輸出“C++”過載函數、建構函式、解構函式,又如在組合程式碼裡呼叫“C””或“C++”函數等。

修飾名由函數名、類名、呼叫約定、返回型別、引數共同決定 。

2.名字修飾約定隨呼叫約定和編譯種類(C或C++)的不同而變化。

1.C編譯時函數名修飾約定規則

_stdcall呼叫約定在輸出函數名前加上一個下劃線字首,後面加上一個“@”符號和其引數的位元組數,格式:_functionname@number;如:_fun@4;

_cdecl呼叫約定僅在輸出函數名前加上一個下劃線字首,格式: _functionname。

_fastcall呼叫約定在輸出函數名前加上一個“@”符號,後面也是一個“@”符號和其引數的位元組數,格式: @functionname@number。

範例:

extern "C"
{
	int __stdcall fun1(int a, int b) { return 0; }
	int __cdecl fun2(int a) { return 0; }
	int __fastcall fun3(int a) { return 0; }
}
int main(void)
{
	fun1(1, 1);
	fun2(2);
	fun3(3);
	return 0;
}

2.C++編譯時函數名修飾約定規則

__stdcall呼叫約定:

1.以"?""標識函數名的開始,後跟函數名;

2.函數名後面以"@@YG"標識參數列的開始,後跟參數列;

3.參數列以代號表示:

代號型別
Xvoid
Dchar
Eunsigned char
Fshort
Hint
Iunsigned int
Jlong
Kunsigned long
Mfloat
Ndouble
_Nbool
PA指標

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

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

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

範例:

extern "C++"
{
	void __stdcall fun(int a) { return 0; }
	int __cdecl fun(int a, int b) { return 0; }
	int __fastcall fun() { return 0; }
}
int main(void)
{
	fun(1);
	fun(2, 3);
	fun();
	return 0;
}

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!  


IT145.com E-mail:sddin#qq.com