函數(shù)調(diào)用、調(diào)用約定和名字約定
調(diào)用約定:
__cdecl __fastcall與 __stdcall,三者都是調(diào)用約定(Calling convention),它決定以下內(nèi)容:
1)函數(shù)參數(shù)的壓棧順序,
2)由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧
3)以及產(chǎn)生函數(shù)修飾名的方法
__cdecl
__cdecl調(diào)用約定又稱為C調(diào)用約定,是 C/C++ 語言缺省的調(diào)用約定。參數(shù)按照從右至左的方式入棧,函數(shù)本身不清理棧,此工作由調(diào)用者負責,返回值在EAX中。
對于 C,__cdecl 命名約定使用前面帶下劃線 (_) 的函數(shù)名;不執(zhí)行任何大小寫轉(zhuǎn)換。對于C++,除非聲明為 extern "C",否則 C++ 函數(shù)將使用不同的名稱修飾方案。
__stdcall
__stdcall調(diào)用約定參數(shù)按照從右至左的方式入棧,函數(shù)自身清理堆棧,返回值在EAX中。
對于 C,__stdcall 命名約定使用前面帶下劃線 (_) 的函數(shù)名,后跟“at”符 (@) 和函數(shù)的參數(shù)大小(以字節(jié)為單位)。不執(zhí)行任何大小寫轉(zhuǎn)換。編譯器使用下列命名約定模板:
_functionname@number
__fastcall
顧名思義,__fastcall 的特點就是快,因為它通過 CPU 寄存器來傳遞參數(shù)。他用 ECX 和 EDX 傳送前兩個雙字(DWORD)或更小的參數(shù),剩下的參數(shù)按照從右至左的方式入棧,函數(shù)自身清理堆棧,返回值在 EAX 中。
__thiscall
這是 C++ 語言特有的一種調(diào)用方式,用于類成員函數(shù)的調(diào)用約定。如果參數(shù)確定,this 指針存放于 ECX 寄存器,函數(shù)自身清理堆棧;如果參數(shù)不確定,this指針在所有參數(shù)入棧后再入棧,調(diào)用者清理棧。__thiscall 不是關(guān)鍵字,程序員不能使用。參數(shù)按照從右至左的方式入棧。
名字修飾約定:
1、修飾名(Decoration name):"C"或者"C++"函數(shù)在內(nèi)部(編譯和鏈接)通過修飾名識別
2、C編譯時函數(shù)名修飾約定規(guī)則:
__stdcall調(diào)用約定在輸出函數(shù)名前加上一個下劃線前綴,后面加上一個"@"符號和其參數(shù)的字節(jié)數(shù),格式為_functionname@number,例如:function(int a, int b),其修飾名為:_function@8
__cdecl調(diào)用約定僅在輸出函數(shù)名前加上一個下劃線前綴,格式為_functionname。
__fastcall調(diào)用約定在輸出函數(shù)名前加上一個"@"符號,后面也是一個"@"符號和其參數(shù)的字節(jié)數(shù),格式為@functionname@number。
3、C++編譯時函數(shù)名修飾約定規(guī)則:
__stdcall調(diào)用約定:
1)、以"?"標識函數(shù)名的開始,后跟函數(shù)名;
2)、函數(shù)名后面以"@@YG"標識參數(shù)表的開始,后跟參數(shù)表;
3)、參數(shù)表以代號表示:
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--表示指針,后面的代號表明指針類型,如果相同類型的指針連續(xù)出現(xiàn),以"0"代替,一個"0"代表一次重復(fù);
4)、參數(shù)表的第一項為該函數(shù)的返回值類型,其后依次為參數(shù)的數(shù)據(jù)類型,指針標識在其所指數(shù)據(jù)類型前;
5)、參數(shù)表后以"@Z"標識整個名字的結(jié)束,如果該函數(shù)無參數(shù),則以"Z"標識結(jié)束。
其格式為"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如
int Test1(char *var1,unsigned long)----"?Test1@@YGHPADK@Z"
void Test2()-----“?Test2@@YGXXZ”
__cdecl調(diào)用約定:
規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開始標識由上面的"@@YG"變?yōu)?/span>"@@YA"。
__fastcall調(diào)用約定:
規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開始標識由上面的"@@YG"變?yōu)?/span>"@@YI"。
VC++對函數(shù)的省缺聲明是"__cedcl",將只能被C/C++調(diào)用.
堆棧
棧:棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。
棧又稱堆棧,是用戶存放程序臨時創(chuàng)建的局部變量,也就是函數(shù)括弧“{}”中定義的變量(但不包括static聲明的變量,)。除此以外,在函數(shù)被調(diào)用時,其參數(shù)也會被壓入發(fā)起調(diào)用的進程棧中,并且待到調(diào)用結(jié)束后,函數(shù)的返回值也會被存放回棧中。由于棧的先進后出特點,所以棧特別方便用來保存/恢復(fù)調(diào)用現(xiàn)場。從這個意義上,可以把堆棧看成一個寄存、交換臨時數(shù)據(jù)的內(nèi)存區(qū)。
棧空間都是在運行時動態(tài)的分配的。
函數(shù)堆棧在運行時動態(tài)分配,原因有二:
一、無法在編譯時候確定函數(shù)運行時所需的堆棧大小;
二、節(jié)省空間
函數(shù)名就是一個指針,指向函數(shù)入口地址,也就是一個代碼段的地址。
在內(nèi)存中,數(shù)據(jù)和指令,堆棧分別在不同的地址段,數(shù)據(jù)段存儲的是程序的數(shù)據(jù),代碼段存儲的是程序的指令,棧段是函數(shù)調(diào)用是保存參數(shù)和調(diào)用是上下文數(shù)據(jù)的內(nèi)存區(qū)。棧由系統(tǒng)自動分配.
函數(shù)名就指向一個代碼段的地址。對數(shù)據(jù)段的內(nèi)存地址進行復(fù)引用操作(*a),得到內(nèi)存中數(shù)據(jù)的值。如果對一個代碼段內(nèi)存地址進行復(fù)引用操作,沒用什么實際意義,不可能返回一個指令。
在標準c文檔(WG14 N1124)里這樣描述
A function designator is an expression that has function type. Except when it is the
operand of the sizeof operator or the unary & operator, a function designator with
type "function returning type" is converted to an expression that has type "pointer to
function returning type".
也就是說函數(shù)類型表達式(如*pf)會被自動轉(zhuǎn)化成函數(shù)指針. 這樣你加多少個*都沒有關(guān)系.
另外對一個函數(shù)指針pf, (*pf)() 和 pf() 都已成為合法的用法 (k&R 不支持 pf())。
C99 的文檔里也提到這一點:
Pointers to functions may be used either as (*pf)() or as pf(). The latter construct, not
sanctioned in K&R, appears in some present versions of C, is unambiguous, invalidates no old
code, and can be an important shorthand. The shorthand is useful for packages that present only
one external name, which designates a structure full of pointers to objects and functions: member
functions can be called as graphics.open(file) instead of
(*graphics.open)(file).
說的是這樣子可以方便調(diào)用結(jié)構(gòu)里的函數(shù)。