天天看點

cdecl, stdcall, pascal,fastcall 調用約定差別

調用約定           壓參數入棧順序     把參數彈出棧者         函數修飾名 

(Calling convention) 

--------------------------------------------------------------------------------------------------------

__cdecl       右->左  被調用者    _function     微機

__cdecl       右->左  被調用者    _function     UNIX

 __fastcall   右->左  被調用者   @[email protected]   

  __stdcall   右->左  被調用者   _

[email protected]

  __pascal   左->右  被調用者  _[email protected]

-----------------------------------------------------------------------------------------------------------

  _cdecl    

  按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于“C”函數或者變量,修飾名是在函數名前加下劃線。對于“C++”函數,有所不同。    

  如函數void   test(void)的修飾名是_test;對于不屬于一個類的“C++”全局函數,修飾名是?test@@ZAXXZ。    

  這是預設調用約定。由于是調用者負責把參數彈出棧,是以可以給函數定義個數不定的參數,如printf函數。    

  _stdcall    

  按從右至左的順序壓參數入棧,由被調用者把參數彈出棧。對于“C”函數或者變量,修飾名以下劃線為字首,然後是函數名,然後是符号“@”及參數的位元組數,如函數int   func(int   a,   double   b)的修飾名是[email protected]。對于“C++”函數,則有所不同。    

  所有的Win32   API函數都遵循該約定。    

  _pascal  

   按從左至右的順序壓參數入棧 ...其它的與_stdcall相同; 

  _fastcall    

  頭兩個DWORD類型或者占更少位元組的參數被放入ECX和EDX寄存器,其他剩下的參數按從右到左的順序壓入棧。由被調用者把參數彈出棧,對于“C”函數或者變量,修飾名以“@”為字首,然後是函數名,接着是符号“@”及參數的位元組數,如函數int   func(int   a,   double   b)的修飾名是@[email protected]。對于“C++”函數,有所不同。    

  未來的編譯器可能使用不同的寄存器來存放參數。    

  thiscall    

  僅僅應用于“C++”成員函數。this指針存放于CX寄存器,參數從右到左壓棧。thiscall不是關鍵詞,是以不能被程式員指定。    

  naked   call    

  采用1-4的調用約定時,如果必要的話,進入函數時編譯器會産生代碼來儲存ESI,EDI,EBX,EBP寄存器,退出函數時則産生代碼恢複這些寄存器的内容。naked   call不産生這樣的代碼。    

  naked   call不是類型修飾符,故必須和_declspec共同使用,如下:    

  __declspec(   naked   )   int   func(   formal_parameters   )    

  {    

  //   Function   body    

  }     

 便于更好了解, 看下面例子(函數調用的過程以彙編代碼表示):      

  void   cdecl       fun1(int   x,int   y);  

  void   stdcall     fun2(int   x,int   y);  

  void   pascal     fun3(int   x,int   y);   

  ****************************************  

  void   cdecl       fun1(int   x,int   y);  

  fun1(x,y);    

  調用   fun1   的彙編代碼  

  push   y  

  push   x  

  call   fun1  

  add     sp,sizeof(x)+sizeof(y)   ;跳過參數區(x,y)  

  fun1   的彙編代碼:  

  fun1   proc    

      push   bp  

      mov     bp,sp  

      ……  

      …  

      pop     bp  

      ret ;傳回,但不跳過參數區  

  fun1   endp  

  ****************************************  

  void   stdcall   fun2(int   x,int   y);  

  fun2(x,y);    

  調用   fun2   的彙編代碼  

  push   y  

  push   x  

  call   fun2  

  fun2   的彙編代碼:  

  fun2   proc    

      push   bp  

      mov     bp,sp  

      ……  

      …  

      pop     bp  

      ret   sizeof(x)+sizeof(y)   ;傳回并跳過參數區(x,y)      

  fun2   endp  

  *****************************************  

  void   pascal     fun3(int   x,int   y);  

  fun3(x,y);    

  調用   fun3   的彙編代碼  

  push   x  

  push   y  

  call   fun3  

  fun3   的彙編代碼:  

  fun3   proc    

      push   bp  

      mov     bp,sp  

      ……  

      …  

      pop     bp  

      ret   sizeof(x)+sizeof(y)   ;傳回并跳過參數區(x,y)      

  fun3   endp   

繼續閱讀