天天看點

__cdecl、__stdcall、__fastcall

1.這三個修飾符的基本意思

__cdecl:C調用方式,VC預設使用該方式,參數從右向左傳遞,參數個數可變,棧的初始和清理由調用者完成

__stdcall:标準調用方式,多種語言使用這種調用方式,參數從右向左傳遞,參數個數不可變,棧的初始和清理由被調用者完成

__fastcall:參數盡量使用寄存器傳遞,很少用到,這裡不予讨論。

2.這三個修飾符的使用位置

這三個修飾符要用在類型名之後,不能用在類型名之前,否則文法無法識别,容易被編譯器誤以為是預設int錯誤。

這三個修飾符都不能用于變量,如果使用會有警告,如下:

編譯器警告(等級 1)C4229

對資料聲明使用 Microsoft 修飾符(如 __cdecl)是已過時的做法。 這三個函數隻能用在函數前面,不能用于别的東西上,可以用在成員函數上,但是這個不像__declspec(dllimport)一樣,__declspec(dllimport)可以用在類聲明的前面,這樣整個類都被導出,包括成員變量和函數,而這幾個調用修飾符隻能直接用在類聲明體中的成員函數的前面(而且函數定義前也需要),來說明成員函數的調用方式。下表是産生規則:

C 和C++ 對應不同的調用約定,産生的修飾符也各不相同,對于函數test(void)如下:

調用約定 extern "C" 或 .c 檔案 .cpp、.cxx 或 /TP

C 命名約定 (__cdecl)

_test

?test@@ZAXXZ

Fastcall 命名約定 (__fastcall)

@[email protected]

?test@@YIXXZ

标準調用命名約定 (__stdcall)

[email protected]

?test@@YGXXZ

第二列,也就是C風格的情況下,@後面的數字表述所有參數的位元組數之和。

第三列,也就是CPP風格的情況下,修飾符有些複雜,以後補充吧。

以前面的一片随筆PureDllApp為例,VC9.0進行分析:

1、extern "C"作用下:

(1)__cdecl作用下(由于VC預設就是這種調用方式,是以這裡就不給出代碼,和前面的一片PureDllApp的代碼一樣):

PureApp.exe對PureDll.dll的導入修飾符如下:

4 fnPureDll

6 nPureDll

1 ??1CPureDll@@[email protected]

3 [email protected]@@[email protected]

0 ??0CPureDll@@[email protected]@Z

5 g_pureDllPureDll.lib的導出修飾符如下:

??0CPureDll@@[email protected]@Z (public: __thiscall CPureDll::CPureDll(int))

??1CPureDll@@[email protected] (public: __thiscall CPureDll::~CPureDll(void))

??4CPureDll@@[email protected]@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))

[email protected]@@[email protected] (public: void __thiscall CPureDll::setValue(int))

_fnPureDll

_g_pureDll

_nPureDllPureDll.dll的導出修飾符如下:

ordinal hint RVA name

1 0 00011005 ??0CPureDll@@[email protected]@Z = @ILT+0(??0CPureDll@@[email protected]@Z)

2 1 000110B9 ??1CPureDll@@[email protected] = @ILT+180(??1CPureDll@@[email protected])

3 2 000110C3 ??4CPureDll@@[email protected]@@Z = @ILT+190(??4CPureDll@@[email protected]@@Z)

4 3 00011168 ?set[email protected]@@[email protected] = @ILT+355([email protected]@@[email protected])

5 4 000110EB fnPureDll = @ILT+230(_fnPureDll)

6 5 00019138 g_pureDll = _g_pureDll

7 6 00019000 nPureDll = _nPureDll(2)__stdcall作用下(調用方式的改變在函數聲明和定義之前都要使用,下面先是發生改變的頭檔案,cpp檔案中在函數定義前做一樣的修改即可):

需要注意的就是調用修飾符隻能用在函數和成員函數的前面,而且要在傳回值類型之後的位置。

// 下列 ifdef 塊是建立使從 DLL 導出更簡單的

// 宏的标準方法。此 DLL 中的所有檔案都是用指令行上定義的 PUREDLL_EXPORTS

// 符号編譯的。在使用此 DLL 的

// 任何其他項目上不應定義此符号。這樣,源檔案中包含此檔案的任何其他項目都會将

// PUREDLL_API 函數視為是從 DLL 導入的,而此 DLL 則将用此宏定義的

// 符号視為是被導出的。

#ifdef __cplusplus

extern "C"

{

#endif

#ifdef PUREDLL_EXPORTS

#define PUREDLL_API __declspec(dllexport)

#else

#define PUREDLL_API __declspec(dllimport)

#endif

// 此類是從 PureDll.dll 導出的

class PUREDLL_API CPureDll {

public:

__stdcall CPureDll(int num);

// TODO: 在此添加您的方法。

__stdcall ~CPureDll(void);

void __stdcall setValue(int value);

private:

int m_num;

int *m_p;

};

extern PUREDLL_API int nPureDll;

extern PUREDLL_API CPureDll g_pureDll;

PUREDLL_API int __stdcall fnPureDll(void);

#ifdef __cplusplus

}

#endif

PureApp.exe對PureDll.dll的導入修飾符如下:

5 g_pureDll

1 ??1CPureDll@@[email protected]

4 [email protected]

3 [email protected]@@[email protected]

0 ??0CPureDll@@[email protected]@Z

6 nPureDllPureDll.lib的導出修飾符如下:

??0CPureDll@@[email protected]@Z (public: __thiscall CPureDll::CPureDll(int))

??1CPureDll@@[email protected] (public: __thiscall CPureDll::~CPureDll(void))

??4CPureDll@@[email protected]@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))

[email protected]@@[email protected] (public: void __stdcall CPureDll::setValue(int))

[email protected]

_g_pureDll

_nPureDllPureDll.dll的導出修飾符如下:

ordinal hint RVA name

1 0 00011005 ??0CPureDll@@[email protected]@Z = @ILT+0(??0CPureDll@@[email protected]@Z)

2 1 000110C3 ??1CPureDll@@[email protected] = @ILT+190(??1CPureDll@@[email protected])

3 2 000110CD ??4CPureDll@@[email protected]@@Z = @ILT+200(??4CPureDll@@[email protected]@@Z)

4 3 0001100F ?se[email protected]@@[email protected] = @ILT+10([email protected]@@[email protected])

5 4 0001105A [email protected] = @ILT+85([email protected])

6 5 00019138 g_pureDll = _g_pureDll

7 6 00019000 nPureDll = _nPureDll

比較上面的兩組之間的差别,就可以發現__cdecl和__stdcall之間的差别。

第一點,構造函數在這兩種調用方式中的産生的修飾符相同,其實産生的修飾符不是這兩種的任何一種,而是__thiscall的修飾符,從PureDll.lib的後面括号中可以看出來,而且VC9.0中在構造函數前面使用__cdecl會直接産生編譯時警告c4166,如下:

編譯器警告(等級 1)C4166

構造函數/析構函數的非法調用約定

構造函數和析構函數不能有非平台預設的調用約定(顯式指定 __clrcall 時除外)。

再對照第一組中setValue函數在PureDll.lib中産生的修飾符,可以知道類成員函數預設使用__thiscall修飾符,即使編譯器選項中選擇了__cdecl,也就是說構造函數和析構函數必須使用目前平台預設的調用方式----__thiscall,其它的成員函數可以通過在函數名前(聲明和定義)強制加上指定的調用方式即可,編譯器選項中的調用方式不行(這裡指定的方式隻是對非成員函數有效的,成員函數會預設使用__thiscall)。

其實__thiscall和__stdcall差别不大,都是被調用者控制棧的初始和清理,隻不過前者的this指針放在寄存器中,而後者的放在棧裡。是以對于成員函數一般不要指定調用方式,使用預設的__shicall即可,除非要做參數數量可變的成員函數,不過這種函數盡量避免。

最後補充上setValue函數的__cdecl調用方式産生的修飾符:

[email protected]@@[email protected] (public: void __cdecl CPureDll::setValue(int))

2.對應上面的extern "C",下面是cpp(去掉extern "C")下對應上面的修飾符:

(1)__cdecl作用下:

PureApp.exe對PureDll.dll的導入修飾符如下:

3 ?fnPureDll@@YAHXZ

5 ?nPureDll@@3HA

1 ??1CPureDll@@[email protected]

6 [email protected]@@[email protected]

0 ??0CPureDll@@[email protected]@Z

4 ?g_pureDll@@3VCPureDll@@APureDll.lib的導出修飾符如下:

??0CPureDll@@[email protected]@Z (public: __thiscall CPureDll::CPureDll(int))

??1CPureDll@@[email protected] (public: __thiscall CPureDll::~CPureDll(void))

??4CPureDll@@[email protected]@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))

?fnPureDll@@YAHXZ (int __cdecl fnPureDll(void))

?g_pureDll@@3VCPureDll@@A (class CPureDll g_pureDll)

?nPureDll@@3HA (int nPureDll)

[email protected]@@[email protected] (public: void __thiscall CPureDll::setValue(int))PureDll.dll的導出修飾符如下:

ordinal hint RVA name

1 0 00011005 ??0CPureDll@@[email protected]@Z = @ILT+0(??0CPureDll@@[email protected]@Z)

2 1 000110B9 ??1CPureDll@@[email protected] = @ILT+180(??1CPureDll@@[email protected])

3 2 000110C3 ??4CPureDll@@[email protected]@@Z = @ILT+190(??4CPureDll@@[email protected]@@Z)

4 3 00011113 ?fnPureDll@@YAHXZ = @ILT+270(?fnPureDll@@YAHXZ)

5 4 00019138 ?g_pureDll@@3VCPureDll@@A = ?g_pureDll@@3VCPureDll@@A(class CPureDll g_pureDll)

6 5 00019000 ?nPureDll@@3HA = ?nPureDll@@3HA (int nPureDll)

7 6 00011168 [email protected]@@[email protected] = @ILT+355([email protected]@@[email protected])(2)__stdcall作用下:

PureApp.exe對PureDll.dll的導入修飾符如下: 3 ?fnPureDll@@YGHXZ

5 ?nPureDll@@3HA

1 ??1CPureDll@@[email protected]

6 [email protected]@@[email protected]

0 ??0CPureDll@@[email protected]@Z

4 ?g_pureDll@@3VCPureDll@@APureDll.lib的導出修飾符如下:

??0CPureDll@@[email protected]@Z (public: __thiscall CPureDll::CPureDll(int))

??1CPureDll@@[email protected] (public: __thiscall CPureDll::~CPureDll(void))

??4CPureDll@@[email protected]@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))

?fnPureDll@@YGHXZ (int __stdcall fnPureDll(void))

?g_pureDll@@3VCPureDll@@A (class CPureDll g_pureDll)

?nPureDll@@3HA (int nPureDll)

[email protected]@@[email protected] (public: void __stdcall CPureDll::setValue(int))PureDll.dll的導出修飾符如下:

ordinal hint RVA name

1 0 00011005 ??0CPureDll@@[email protected]@Z = @ILT+0(??0CPureDll@@[email protected]@Z)

2 1 000110BE ??1CPureDll@@[email protected] = @ILT+185(??1CPureDll@@[email protected])

3 2 000110C8 ??4CPureDll@@[email protected]@@Z = @ILT+195(??4CPureDll@@[email protected]@@Z)

4 3 00011118 ?fnPureDll@@YGHXZ = @ILT+275(?fnPureDll@@YGHXZ)

5 4 00019138 ?g_pureDll@@3VCPureDll@@A = ?g_pureDll@@3VCPureDll@@A(class CPureDll g_pureDll)

6 5 00019000 ?nPureDll@@3HA = ?nPureDll@@3HA (int nPureDll)

7 6 0001100F ?se[email protected]@@[email protected] = @ILT+10([email protected]@@[email protected])

補充一個強制指定__cdecl的setValue的修飾符:

[email protected]@@[email protected] (public: void __cdecl CPureDll::setValue(int))

cpp的__stdcall也沒有什麼特别的,和extern "C"相比,沒有什麼大的變化,唯一變得就是後面的字母變多了。而且能夠發現無論是否存在extern "C",成員函數的修飾符都沒有改變,這是因為C語言本身沒有成員函數,是以extern "C對成員函數是無效的。

是以成員函數有兩個特殊點:首先是extern "C"對類無效,即成員函數無效,其次是成員函數預設是__thiscall,除非在函數前強制指明其他調用方式。

暫時到這裡,以後有機會再補充别的。