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,除非在函數前強制指明其他調用方式。
暫時到這裡,以後有機會再補充别的。