天天看點

調用約定(Calling convention)詳解(__stdcall,__cdecl,__fastcall)

#define   CALLBACK         __stdcall   

#define   WINAPI             __stdcall   

#define   WINAPIV           __cdecl   

#define   APIENTRY          WINAPI   

#define   APIPRIVATE       __stdcall   

#define   PASCAL             __stdcall   

     調用約定(Calling   convention):決定函數參數傳送時入棧和出棧的順序,由調用者還是被調用者把參數彈出棧,以及編譯器用來識别函數名字的修飾約定。   

函數調用約定有多種,這裡簡單說一下:  

        1、__stdcall調用約定相當于16位動态庫中經常使用的PASCAL調用約定。在32位的VC++5.0中PASCAL調用約定不再被支援(實際上它已被定義為__stdcall。除了__pascal外,__fortran和__syscall也不被支援),取而代之的是__stdcall調用約定。兩者實質上是一緻的,即函數的參數自右向左通過棧傳遞,被調用的函數在傳回前清理傳送參數的記憶體棧,但不同的是函數名的修飾部分(關于函數名的修飾部分在後面将詳細說明)。  

          _stdcall是Pascal程式的預設調用方式,通常用于Win32   Api中,函數采用從右到左的壓棧方式,自己在退出時清空堆棧。VC将函數編譯後會在函數名前面加上下劃線字首,在函數名後加上"@"和參數的位元組數。  

          2、C調用約定(即用__cdecl關鍵字說明)按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于傳送參數的記憶體棧是由調用者來維護的(正因為如此,實作可變參數的函數隻能使用該調用約定)。另外,在函數名修飾約定方面也有所不同。  

          _cdecl是C和C++程式的預設調用方式。每一個調用它的函數都包含清空堆棧的代碼,是以産生的可執行檔案大小會比調用_stdcall函數的大。函數采用從右到左的壓棧方式。VC将函數編譯後會在函數名前面加上下劃線字首。是MFC預設調用約定。  

          3、__fastcall調用約定是“人”如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數的(實際上,它用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在傳回前清理傳送參數的記憶體棧),在函數名修飾約定方面,它和前兩者均不同。  

          _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。

總結一點常用的:     

      關于PASCAL這種調用約定的函數都是由它本身來清棧,而__cdecl的函數都是由調用者來清棧. 實際用的時候,個人覺得兩者最大的差别在于:__cdecl的函數參數個數可以聲明為不确定,比如printf,scanf之類,而PASCAL的函數是不可以這樣做的,如果這樣的話它不知道實參有多少個。  

      VC裡面:PASCAL==CALLBACK==WINAPI==__stdcall

繼續閱讀