天天看點

15、__stdcall,、__cdecl,thiscall等宏

1、(Microsoft Specific)__stdcall主要指明了恢複堆棧的規則:在主調用函數中負責壓棧,在被調用函數中負責彈出堆棧中的參數,并且負責恢複堆棧。<?xml:namespace prefix = o />

The __stdcall calling convention is used to call Win32 API functions. The callee(被調用者) cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype. The following list shows the implementation of this calling convention.

Element

Implementation

Argument-passing order

Right to left.

Argument-passing convention

By value, unless a pointer or reference type is passed.

Stack-maintenance responsibility

Called function pops its own arguments from the stack.

Name-decoration convention

An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12

Case-translation convention

None

The /Gz compiler option specifies __stdcall for all functions not explicitly declared with a different calling convention. 

Functions declared using the __stdcall modifier return values the same way as functions declared using __cdecl.

Example

In the following example, use of __stdcall results in all WINAPI function types being handled as a standard call:

// Example of the __stdcall keyword

#define WINAPI __stdcall

__cdecl和__stdcall最大的差別在于:使用__stdcall調用方式的函數在傳回以前要把傳給它的參數彈出堆棧,而使用__cdecl調用方式的函數可以直接傳回,由調用它的函數來将用過的參數彈出堆棧。

2、調用約定:

__cdecl預設

是BorlandC++的預設的C格式命名約定,它在辨別符前加一下劃線,以保留它原來所有的全程辨別符。參數按最右邊參數優先的原則傳遞給棧,然後清棧。

extern"C "bool __cdecl TestFunction();

在def檔案中顯示為 

TestFunction @1

注釋:@1表示函數的順序數,将在“使用别名”時使用。

__pascalPascal格式

這時函數名全部變成大寫,第一個參數先壓棧,然後清棧。

TESTFUNCTION @1 //deffile

__stdcall标準調用

最後一個參數先壓棧,然後清棧。

TestFunction @1 //deffile

__fastcall把參數傳遞給寄存器

第一個參數先壓棧,然後清棧。

@TestFunction @1 //deffile

3、在VC中!

__cdecl無特征,隻輸出函數名

__stdcall的函數輸出前會帶一 "_"字尾帶 "@nn "

__fastcall函數輸出前帶一 "@ "字尾帶 "@nn

4、調用約定

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。

參考

  [2] MSDN