匯編說明__cdecl,__stdcall,__fastcall的差異
1.如果函數func是__cdecl(C/C++默認調用方式,實現可變參數函數),調用時情況如下
int main()
{
//參數從右到左壓棧
push 4
push 3
push 2
push 1
call func
add esp 0x10 //調用者恢復堆棧指針esp,4個參數的大小是0x10(4x4)
}
2.如果函數func是__stdcall(最多語言支持的方式,COM規范標準),調用時情況如下
int main()
{
//參數從右到左壓棧
push 4
push 3
push 2
push 1
call func
//恢復堆棧指針由被調用者func負責,方法是"ret 0x10"
}
3.如果函數func是__pascal,調用情況如下
int main()
{
//參數從左到右壓棧
push 1
push 2
push 3
push 4
call func
//恢復堆棧指針由被調用者func負責,方法是"ret 0x10"
}
3.如果函數func是__fastcall(寄存器傳參),調用情況如下
int main()
{
//參數先用ecx, edx, eax傳遞,然后再壓棧
//不進棧
//(不知為什么,幫助中寫的是從左到右傳遞的,
//是不是錯了,還是bcb6和bcb5的不一樣)
push 4
mov ecx 3
mov edx 2
mov eax 1
call func
//恢復堆棧指針由被調用者func負責,方法是"ret 0x04",
//因為只進棧一個參數,其余用寄存器傳遞,所以用ret 0x04恢復
}
發表者:huang_jh
#define callback __stdcall
#define winapi __stdcall
定義成不同的名字只是為了"望文知意"就像hwnd和hcursor是一樣的類型.
他們都是窗口函數(過程)......
發表者:sinman
我收集的,全仍上來了
左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,但不同的是函數名的修飾部分。
_stdcall是pascal程序的缺省調用方式,通常用于win32 api中,函數采用從右到左的壓棧方式,自己在退出時清空堆棧。vc將函數編譯后會在函數名前面加上下劃線前綴,在函數名后加上"@"和參數的字節數。
2、c調用約定按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于傳送參數的內存棧是由調用者來維護的。另外,在函數名修飾約定方面也有所不同。
_cdecl是c和c++程序的缺省調用方式。每一個調用它的函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用_stdcall函數的大。函 數采用從右到左的壓棧方式。vc將函數編譯后會在函數名前面加上下劃線前綴。是mfc缺省調用約定。
3、__fastcall調用約定是“人”如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數的或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧),在函數名修飾約定方面,它和前兩者均不同。
_fastcall方式的函數采用寄存器傳遞參數,vc將函數編譯后會在函數名前面加上"@"前綴,在函數名后加上"@"和參數的字節數。
4、thiscall僅僅應用于“c++”成員函數。this指針存放于cx寄存器,參數從右到左壓。thiscall不是關鍵詞,因此不能被程序員指定。
5、naked call采用1-4的調用約定時,如果必要的話,進入函數時編譯器會產生代碼來保存esi,edi,ebx,ebp寄存器,退出函數時則產生代碼恢復這些寄存器的內容。naked call不產生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。
關鍵字 __stdcall、__cdecl和__fastcall可以直接加在要輸出的函數前,也可以在編譯環境的setting...\c/c++ \code generation項選擇。當加在輸出函數前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。它們對應的命令行參數分別為/gz、 /gd和/gr。缺省狀態為/gd,即__cdecl。
要完全模仿pascal調用約定首先必須使用__stdcall調用約定,至于函數名修飾約定,可以通過其它方法模仿。還有一個值得一提的是winapi 宏,windows.h支持該宏,它可以將出函數翻譯成適當的調用約定,在win32中,它被定義為__stdcall。使用winapi宏可以創建自己 的apis。
2)名字修飾約定
1、修飾名(decoration name)
“c”或者“c++”函數在內部通過修飾名識別。修飾名是編譯器在編譯函數定義或者原型時生成的字符串。有些情況下使用函數的修飾名是必要的,如在模塊定義文件里頭指定輸出“c++”重載函數、構造函數、析構函數,又如在匯編代碼里調用“c””或“c++”函數等。
修飾名由函數名、類名、調用約定、返回類型、參數等共同決定。
2、名字修飾約定隨調用約定和編譯種類(c或c++)的不同而變化。函數名修飾約定隨編譯種類和調用約定的不同而不同,下面分別說明。
a、c編譯時函數名修飾約定規則:
__stdcall調用約定在輸出函數名前加上一個下劃線前綴,后面加上一個“@”符號和其參數的字節數,格式為_functionname@number。
__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式為_functionname。
__fastcall調用約定在輸出函數名前加上一個“@”符號,后面也是一個“@”符號和其參數的字節數,格式為@functionname@number。
它們均不改變輸出函數名中的字符大小寫,這和pascal調用約定不同,pascal約定輸出的函數名無任何修飾且全部大寫。
b、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-----“?test1@@yghpadk@z”
void test2 -----“?test2@@ygxxz”
__cdecl調用約定:
規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的“@@yg”變為“@@ya”。
__fastcall調用約定:
規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的“@@yg”變為“@@yi”。
vc++對函數的省缺聲明是"__cedcl",將只能被c/c++調用.
cb在輸出函數聲明時使用4種修飾符號
//__cdecl
cb的默認值,它會在輸出函數名前加_,并保留此函數名不變,參數按照從右到左的順序依次傳遞給棧,也可以寫成_cdecl和cdecl形式。
//__fastcall
她修飾的函數的參數將盡肯呢感地使用寄存器來處理,其函數名前加@,參數按照從左到右的順序壓棧;
//__pascal
它說明的函數名使用pascal格式的命名約定。這時函數名全部大寫。參數按照從左到右的順序壓棧;
//__stdcall
使用標準約定的函數名。函數名不會改變。使用__stdcall修飾時。參數按照由右到左的順序壓棧,也可以是_stdcall;
發表者:echoher
far是古代的東西
在16位模式下,指針是16位的
指針的尋址空間只有64k
如果指定far,說明這個指針指向的地址要加上基地址
就是說用far可以指定64k以外的區域
現在已經沒用了
一點用也沒有了
posted on 2011-01-10 13:38 肥仔 閱讀(1507) 評論(1) 編輯 收藏 引用 所屬分類: C++ 基礎