天天看點

C/C++ Calling covention 調用約定:_stdcall _cdecl CALLBACK WINAPI PASCAL

宏定義:

#define  CALLBACK    __stdcall

#define  WINAPI      __stdcall

#define  WINAPIV     __cdecl

#define  APIENTRY    WINAPI

#define  APIPRIVATE  __stdcall

#define  PASCAL      __stdcall

調用約定(Calling convention):1、決定函數參數傳送時入棧和出棧的順序;

                                                  2、由調用者還是被調用者把參數彈出棧;

                                                  3、以及編譯器用來識别函數名字的修飾約定。

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

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

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

    2、C Calling convention 調用約定(即用__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。

繼續閱讀