__cdecl
C++
void fun(int a) //默認__cdecl
{
cout << a;
}
int main()
{
fun(3);
system("pause");
return 0;
}
反彙編:
void fun(int a)
{
00D713A0 push ebp
00D713A1 mov ebp,esp
00D713A3 sub esp,0C0h
00D713A9 push ebx
00D713AA push esi
00D713AB push edi
00D713AC lea edi,[ebp-0C0h]
00D713B2 mov ecx,30h
00D713B7 mov eax,0CCCCCCCCh
00D713BC rep stos dword ptr es:[edi]
cout << a;
00D713BE mov esi,esp
00D713C0 mov eax,dword ptr [a]
00D713C3 push eax
00D713C4 mov ecx,dword ptr [__imp_std::cout (0D78294h)]
00D713CA call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D78290h)]
00D713D0 cmp esi,esp
00D713D2 call @ILT+305(__RTC_CheckEsp) (0D71136h)
}
00D713D7 pop edi
00D713D8 pop esi
00D713D9 pop ebx
00D713DA add esp,0C0h
00D713E0 cmp ebp,esp
00D713E2 call @ILT+305(__RTC_CheckEsp) (0D71136h)
00D713E7 mov esp,ebp
00D713E9 pop ebp
00D713EA ret
//main中調用fun
fun(3);
0005141E push 3
00051420 call fun (511D1h)
00051425 add esp,4
__cdecl :由調用者清堆棧
__stdcall
c++:
void _stdcall fun(int a)
{
cout << a;
}
int main()
{
fun(3);
system("pause");
return 0;
}
反彙編:
void _stdcall fun(int a)
{
003E13A0 push ebp
003E13A1 mov ebp,esp
003E13A3 sub esp,0C0h
003E13A9 push ebx
003E13AA push esi
003E13AB push edi
003E13AC lea edi,[ebp-0C0h]
003E13B2 mov ecx,30h
003E13B7 mov eax,0CCCCCCCCh
003E13BC rep stos dword ptr es:[edi]
cout << a;
003E13BE mov esi,esp
003E13C0 mov eax,dword ptr [a]
003E13C3 push eax
003E13C4 mov ecx,dword ptr [__imp_std::cout (3E8294h)]
003E13CA call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (3E8290h)]
003E13D0 cmp esi,esp
003E13D2 call @ILT+305(__RTC_CheckEsp) (3E1136h)
}
003E13D7 pop edi
003E13D8 pop esi
003E13D9 pop ebx
003E13DA add esp,0C0h
003E13E0 cmp ebp,esp
003E13E2 call @ILT+305(__RTC_CheckEsp) (3E1136h)
003E13E7 mov esp,ebp
003E13E9 pop ebp
003E13EA ret 4
//main中調用fun
fun(3);
00AA141E push 3
00AA1420 call fun (0AA11CCh)
__stdcall:由被調用函數清堆棧
幾種函數調用方式
_cdecl 是CDeclaration的縮寫,表示C語言默認的函數調用方法:所有參數從右到左依次入棧,這些參數由調用者清除,稱爲手動清棧。被調用函數不需要求調用者傳遞多少參數,調用者傳遞過多或者過少的參數,甚至完全不同的參數都不會產生編譯階段的錯誤。
_stdcall 是StandardCall的縮寫,是C++的標準調用方式:所有參數從右到左依次入棧,如果是調用類成員的話,最後一個入棧的是this指針。這些堆棧中的參數由被調用的函數在返回後清除,使用的指令是 retnX,X表示參數佔用的字節數,CPU在ret之後自動彈出X個字節的堆棧空間。稱爲自動清棧。函數在編譯的時候就必須確定參數個數,並且調用者必須嚴格的控制參數的生成,不能多,不能少,否則返回後會出錯。
PASCAL 是Pascal語言的函數調用方式,也可以在C/C++中使用,參數壓棧順序與前兩者相反。返回時的清棧方式忘記了。。。
_fastcall是編譯器指定的快速調用方式。由於大多數的函數參數個數很少,使用堆棧傳遞比較費時。因此_fastcall通常規定將前兩個(或若干個)參數由寄存器傳遞,其餘參數還是通過堆棧傳遞。不同編譯器編譯的程序規定的寄存器不同。返回方式和_stdcall相當。
_thiscall 是爲了解決類成員調用中this指針傳遞而規定的。_thiscall要求把this指針放在特定寄存器中,該寄存器由編譯器決定。VC使用ecx,Borland的C++編譯器使用eax。返回方式和_stdcall相當。
_fastcall 和 _thiscall涉及的寄存器由編譯器決定,因此不能用作跨編譯器的接口。所以Windows上的COM對象接口都定義爲_stdcall調用方式。
C中不加說明默認函數爲_cdecl方式(C中也只能用這種方式),C++也一樣,但是默認的調用方式可以在IDE環境中設置。
帶有可變參數的函數必須且只能使用_cdecl方式,例如下面的函數:
int printf(char * fmtStr, ...);
int scanf(char * fmtStr, ...);
*/幾種調用約定的區別
[編輯本段]
幾種調用約定的區別
__cdecl __fastcall與 __stdcall,三者都是調用約定(Calling convention),它決定以下內容:1)函數參數的壓棧順序,2)由調用者還是被調用者把參數彈出棧,3)以及產生函數修飾名的方法。
1、__stdcall調用約定:函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,
2、_cdecl是C和C++程序的缺省調用方式。每一個調用它的函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用_stdcall函數的大。函數採用從右到左的壓棧方式。注意:對於可變參數的成員函數,始終使用__cdecl的轉換方式。
3、__fastcall調用約定:它是通過寄存器來傳送參數的(實際上,它用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧)。
4、thiscall僅僅應用於"C++"成員函數。this指針存放於CX寄存器,參數從右到左壓。thiscall不是關鍵詞,因此不能被程序員指定。
5、nakedcall採用1-4的調用約定時,如果必要的話,進入函數時編譯器會產生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數時則產生代碼恢復這些寄存器的內容。naked call不產生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。
[編輯本段]
名字修飾約定
1、修飾名(Decoration name):"C"或者"C++"函數在內部(編譯和鏈接)通過修飾名識別
2、C編譯時函數名修飾約定規則:
__stdcall調用約定在輸出函數名前加上一個下劃線前綴,後面加上一個"@"符號和其參數的字節數,格式爲_functionname@number,例如 :function(int a, int b),其修飾名爲:_function@8
__cdecl調用約定僅在輸出函數名前加上一個下劃線前綴,格式爲_functionname。
__fastcall調用約定在輸出函數名前加上一個"@"符號,後面也是一個"@"符號和其參數的字節數,格式爲@functionname@number。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/leonardWang/archive/2010/01/24/5248747.aspx