高級語言翻譯成機器碼後,計算機沒有辦法知道函數調用的參數個數、類型,也沒有硬件可以保護這些參數。
另外,在C++中,因為重載的原因,所以對函數的命名方式和普通C語言並不一致,該方式稱為名字改編。
函數調用者與函數之間,尤其是跨語言調用接口時,需要一個協議約定來傳遞參數——棧。
關鍵流程:
調用時,調用者依次把參數壓棧,然後調用函數,
被調用函數,在堆棧中取得數據,並進行計算。
函數計算結束以後,或者調用者、或者函數本身修改堆棧,使堆棧恢復原裝。
常見的函數調用約定:
關鍵字__cdecl、__stdcall和__fastcall可以直接加在要輸出的函數前,
也可以在編譯環境的Setting...-> C/C++->Code Generation項選擇。
它們對應的命令行參數分別為/Gd、/Gz和/Gr。缺省狀態為/Gd,即__cdecl。
當加在輸出函數前的關鍵字與編譯 環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。
其中有 2 個關鍵問題:
入棧順序上,一般都是按照從右往左入棧。
關鍵分歧在於誰負責清空,分別對應 2 個典型的約定:stdcall, cdecl
stdcall 約定——pascal調用約定。WINAPI, CALLBACK
cdecl 約定——C調用約定,C語言默認
函數本身不清理堆棧,調用者負責清理堆棧。由於這種變化,C調用約定允許函數的參數的個數是不固定的。
由於參數按照從右向左順序壓棧,因此最開始的參數在最接近棧頂的位置,因此
當采用不定個數參數時,第一個參數在棧中的位置肯定能知道,只要不定的參數個數能夠根據第一個或者後續的明確的參數確定下來,就可以使用不定參數,
例如 sprintf 函數,
int sprintf(char* buffer,const char* format,...)
由於所有的不定參數都可以通過format確定,因此使用不定個數的參數是沒有問題的。
thiscall
唯一不能明確指明的函數修飾,
因為thiscall不是關鍵字。它是C++類成員函數缺省的調用約定。
由於成員函數調用還有一個this指針,因此必須特殊處理,
WINAPI
可以被翻譯成適當的調用約定以供函數使用。該宏定義於windef.h之中。下面是在windef.h中的部分內容:
#define CDECL _cdecl #define WINAPI CDECL #define CALLBACK __stdcall #define WINAPI __stdcall #define APIENTRY WINAPI
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”。
__fastcall調用約定:
規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的“@@YG”變為“@@YI”。
VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用.
如果定義的約定和使用的約定不一致,則將導致堆棧被破壞。
百度上一搜一大片的介紹資料。不過初學者沒必要知道,慢慢來吧,很多東西你沒有使用經驗很難理解。
A)3
是下面這三個
f2(u1,u2) //函數表達式
(u3,u4) //逗號表達式
(u6,max(u7,u8)) //逗號表達式