Visual C++ 编译器参数传递和命名约定——关于__cdecl和__stdcall

来源:互联网 发布:linux系统分区 编辑:程序博客网 时间:2024/06/07 08:41


通过 Visual C++编译器,您可以为传递参数指定约定并返回函数和调用方之间的值。 并非所有支持的平台上都有所有约定,因此某些约定使用特定平台的实现。 在大多数情况下,忽略指定特定平台上不受支持约定的关键字或编译器开关,和使用平台默认约定。

在 x86 平台上,在通过时,所有参数扩展到 32 位。 回归值也扩展为 32 位,并且返回 EAX 寄存器,除了 8 字节结构返回 DEX:EAX 寄存器。 更大的结构在 EAX 寄存器返回,作为到隐藏的返回结构的指针。 参数从右向左被推入堆栈中。 不是 POD 的结构不会返回到寄存器中。

该编译器生成 prolog和 epilog 编码来保存和还原 ESI、 EDI、和 EBX EBP 寄存器,如果这些在函数中使用。

说明:当结构、联合或类从函数返回值时,该类型的所有定义需要相同,或者该过程可能会在运行时失败。

有关如何定义自己的prolog 函数和 epilog 代码的信息,请参见裸函数调用。有关以 x64 平台为目标的代码中的默认调用约定的信息,请参阅 x64调用约定概述。 有关针对 ARM 平台的代码中的调用约定问题的信息,请参阅 Visual C++ARM迁移的常见问题

可视化 C/C++ 编译器支持以下调用约定。

关键字

堆栈”清理

参数传递

__cdecl

调用方

将参数压入堆栈,按照相反的顺序(从右到左)

__clrcall

顺序加载参数到 CLR 表达式堆栈上(从左到右)。

stdcall

被调用方

将参数压入堆栈,按照相反的顺序(从右到左)

__fastcall

被调用方

存储在寄存器,然后推入堆栈

__thiscall

被调用方

压入堆栈:this 指针存储在 ECX 中

__vectorcall

被调用方

存储在寄存器,然后以相反顺序推入堆栈(从右到左)

__cdecl

__cdecl 是 C 和 C++ 程序的默认调用约定。 由于堆栈已有调用方清理,因此它可以执行 vararg 函数。 由于每个函数调用均要求包括堆栈清理代码,__cdecl 调用约定需比__stdcall 调用约定创建较大的可执行文件。 以下列表显示此调用约定的实现。

元素

实现

参数传递顺序

从右向左。

堆栈维护职责

调用函数在堆栈中弹出参数。

名称修饰约定

除了在导出使用 C 链接的 __cdecl 功能的情况下,下划线字符 (_) 都是名称的前缀。

用例转换约定

不执行任何大小写转换。

在 ARM 和 x64 处理器上,__cdecl 由编译器接受和忽略;在ARM 和 x64 体系结构上,按照约定,如果可能,变量将传入寄存器,且后面的参数在堆栈上传递。

将 __cdecl 修饰符放置在变量或者函数名称前面。 由于 C 命名和调用转换为默认,因此,只有当您指定 /Gv (vectorcall)、/Gz (stdcall) 或 /Gr (fastcall) 编译器选项时才需要使用 __cdecl。 /Gd 编译器选项强制 __cdecl 调用约定。

对于非静态函数类,如果函数是超行定义的,则调用约定修饰符不必在超行定义中指定。 用于非静态成员方法类,也就是说,假定声明时具有的泛型是指定的调用约定。 给定此类定义:

struct CMyClass {

   void __cdecl mymethod();

};

如下:

void CMyClass::mymethod() { return; }

等效于此:

void __cdecl CMyClass::mymethod() { return;}

示例

在下面的示例中,将指示编译器使用 C 命名和调用 system 函数约定。

// Example of the __cdecl keyword on function

int __cdecl system(constchar *);

// Example of the __cdecl keyword on function pointer

typedef BOOL (__cdecl *funcname_ptr)(void * arg1,const char * arg2, DWORDflags, ...);

__stdcall

__stdcall 调用约定用来调用Win32 API 函数。 被调用方清理堆栈,因此,该编译器进行 vararg 函数 __cdecl。 使用此调用约定的函数需要一个函数原型。

return-type __stdcall function-name[(argument-list)]

以下列表显示此调用约定的实现。

元素

实现

参数传递顺序

从右向左。

参数传递约定

除非指针或引用类型通过,否则使用此值。

堆栈维护职责

被调用函数在堆栈中弹出自己的参数。

名称修饰约定

下划线 (_)作为名称的前缀。 该名称跟在后接函数列表的字节数(以十进制为单位)的符号 (@) 后。 因此,声明为 int func( int a, double b ) 的函数修饰如下所示:_func@12

用例转换约定

/Gz 编译器选项为所有没有用不同调用约定显式声明的函数指定 __stdcall

使用 __stdcall 修饰符声明的函数与使用 __cdecl 声明的函数以相同的方式返回值。

在 ARM 和 x64 处理器上,__stdcall 由编译器接受并忽略;在ARM 和 x64 体系结构上,按照约定,如果可能,变量将传入寄存器,且后面的参数在堆栈上传递。

对于非静态函数类,如果函数是超行定义的,则调用约定修饰符不必在超行定义中指定。 用于非静态成员方法类,也就是说,假定声明时具有的泛型是指定的调用约定。 给定此类定义,

struct CMyClass {

   void __stdcall mymethod();

};

this

void CMyClass::mymethod() { return; }

等效于此

void __stdcall CMyClass::mymethod() {return; }

示例

在下面的示例中,stdcall 的使用生成所有的作为标准调用的已处理的 WINAPI 函数类型:

//Example of the __stdcall keyword

#defineWINAPI __stdcall

//Example of the __stdcall keyword on function pointer

typedef BOOL (__stdcall *funcname_ptr)(void * arg1,const char * arg2, DWORD flags, ...);

 

 参考文献:

http://msdn.microsoft.com/zh-cn/library/984x0h58.aspx


0 0