您的位置:首页 > 编程语言 > C语言/C++

C/C++中函数调用规则(约定)__cdecl __stdcall __thiscall __vectorcall __fastcall __clrcall

2017-02-06 10:54 921 查看
转自https://my.oschina.net/ray1421/blog/699540

相关文献

__stdcall
https://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

C语言函数可变参数详解 - ranpanf的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/ranpanf/article/details/4693130

关于c++变长参数列表总结 - zray4u的个人空间 -自己转载的另一篇文章
http://my.oschina.net/ray1421/blog/699538

注意平时我们所说的参数传递顺序和参数入栈顺序是一回事。

函数调用时,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针恢复到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果。
https://yq.aliyun.com/articles/34353
_cdecl  这个名字起的难道是 C default calling的意思

__cdecl is the default calling convention(默认的调用约定) for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions.(这里提供了一条非常重要的信息:只有由调用者来清理堆栈的函数才能够声明为变参函数) The __cdecl calling
convention creates larger executables than __stdcall,(这种方式生成的可执行程序要比_stdcall方式生成的可执行文件要大) because it requires each function call to
include stack cleanup code. The following list shows the implementation of this calling convention.下面的表格中主要包含:

参数传递顺序:从右向左   (注:注意区别参数传递顺序与参数计算顺序,在c++中并没有规定参数计算顺序。) 

cout和printf的缓冲机制 / 蓝讯  http://www.lxway.com/66081202.htm

堆栈维护责任:调用函数将参数从堆栈中pop.

名称修饰约定:指的是根据何种算法来为每个函数生成一个唯一的名字

case-translation convention:?????





On ARM and x64 processors, __cdecl is accepted but typically ignored by the compiler. By convention on ARM and x64, arguments are passed in registers when possible, and subsequent arguments are passed on the stack. In x64 code,
use __cdecl to override the /Gv compiler option and use the default x64 calling convention.

For non-static class functions, if the function is defined out-of-line, the calling convention modifier does not have to be specified on the out-of-line definition. That is, for class non-static member methods, the calling convention specified during
declaration is assumed at the point of definition.

对于非静态类成员函数,如果函数的定义不一致,调用方式修饰符并不一定要在不一致的定义中指定,即对于类中的非静态成员函数,在声明时指定的调用方式约定将在定义时呈现。

Given this class definition:

struct CMyClass { void __cdecl mymethod(); };

this:

void CMyClass::mymethod() { return; }   //即只要在声明中指定即可,而不要在定义再重新声明,防止造成不一致,而适得其反。

is equivalent to this:

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

Example

In the following example, the compiler is instructed to use C naming and calling conventions for the system function.

// Example of the __cdecl keyword on function

int __cdecl system(const char *);

// Example of the __cdecl keyword on function pointer

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

===========================

The document is archived and information here might be outdated

__stdcall

Microsoft Specific

The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack(被调用者清理堆栈。note:这也是没办法的事,因为如果用户程序调用win32 API, 由于windows内部实现的不了解,无法完成相关的清理工作,即使微软提供了相关的实现说明,由用户来承担相关的清理工作也可能是一项繁琐的工作,甚至可能造成系统的不稳定。),
so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype.

 

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

The following list shows the implementation of this calling convention.





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

对于所有没有显式用其他调用约定声明的函数,Gz编译器默认指定为__stdcall.(note:前面不是说__cdecl是C和c++默认的调用方式吗?)

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

On ARM and x64 processors, __stdcall is accepted and ignored by the compiler; on ARM and x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.

For non-static class functions, if the function is defined out-of-line, the calling convention modifier does not have to be specified on the out-of-line definition. That is, for class non-static member methods, the calling convention specified during
declaration is assumed at the point of definition. Given this class definition,

上面这两段和下面这一段与前面介绍__cdecl中的内容大致相同。

C++

struct CMyClass { void __stdcall mymethod(); };

this

C++

void CMyClass::mymethod() { return; }

is equivalent to this

C++

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


Example

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

C  //操作系统函数一般者是用c语言编写。

// Example of the __stdcall keyword

#define WINAPI __stdcall

// Example of the __stdcall keyword on function pointer

//这里…是表示变参的意思吗???

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

=================================


__fastcall

Microsoft Specific

The __fastcall calling convention specifies that arguments to functions are to be passed in registers, (note:用寄存器传参确实会比较快)when possible. This calling convention only applies to the x86 architecture. (note:对于x64不需要指定就是这样,上文有提及)The
following list shows the implementation of this calling convention.

__fastcall的前两个参数会放入ecx,edx  (下面表格中第一行也有说明)





Future compiler versions may use different registers to store parameters.

Using the /Gr compiler option causes each function in the module to compile as __fastcall unless the function is declared
by using a conflicting attribute, or the name of the function is main.

The __fastcall keyword is accepted and ignored by the compilers that target ARM and x64 architectures; on an x64 chip, by convention, the first four arguments are passed in registers when possible, and additional arguments
are passed on the stack. On an ARM chip, up to four integer arguments and eight floating-point arguments may be passed in registers, and additional arguments are passed on the stack.(note:在arm中能够用寄存器传递的参数的个数也是受限的,额外的参数仍然在栈上传递)

C++

struct CMyClass {
void __fastcall mymethod();
};

this:

C++

void CMyClass::mymethod() { return; }

is equivalent to this:

C++

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

Example

In the following example, the function DeleteAggrWrapper is passed arguments in registers:

C

// Example of the __fastcall keyword
#define FASTCALL    __fastcall

void FASTCALL DeleteAggrWrapper(void* pWrapper);
// Example of the __ fastcall keyword on function pointer
typedef BOOL (__fastcall *funcname_ptr)(void * arg1, const char * arg2, DWORD flags, ...);

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

__thiscall  C++成员函数专用,不支持变参。

The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans
the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX(note:???????), and not on the stack, on the x86 architecture.

One reason to use __thiscall is in classes whose member functions use __clrcall by default. In that case, you can use __thiscall to make individual member functions callable from
native code.(基本不会用到)

When compiling with /clr:pure, all functions and function pointers are __clrcall unless specified otherwise.

In releases before Visual C++ 2005, the thiscall calling convention could not be explicitly specified in a program, because thiscall was not a keyword.

vararg member functions use the __cdecl calling convention. All function arguments are pushed on the stack, with the this pointer placed on the stack last.(note:并不是所有的成员函数只能指定为__thiscall函数调用方式,对于变参成员函数,调用方式使用的是__cdecl,而且也只能是这个。this指针是类的非静态成员函数的一个隐含的参数,它被放在函数堆栈中是所有参数中最后一个被放入堆栈的。)

Because this calling convention applies only to C++, there is no C name decoration scheme.

On ARM and x64 machines, __thiscall is accepted and ignored by the compiler.

For non-static class functions, if the function is defined out-of-line, the calling convention modifier does not have to be specified on the out-of-line definition. That is, for class non-static member methods, the calling convention specified during
declaration is assumed at the point of definition.

Example

// thiscall_cc.cpp
// compile with: /c /clr:oldSyntax
struct CMyClass {
void __thiscall mymethod();
void __clrcall mymethod2();
};

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

 

 

__vectorcall

Microsoft Specific

The __vectorcall calling convention specifies that arguments to functions are to be passed in registers, when possible. __vectorcall uses more registers for arguments than __fastcall or
the default x64 calling convention use. The __vectorcall calling convention is only supported in native code on x86 and x64 processors
that include Streaming SIMD Extensions 2 (SSE2) and above. Use __vectorcall to speed functions that pass several floating-point or SIMD vector arguments and perform operations that take advantage of the arguments loaded in registers.
The following list shows the features that are common to the x86 and x64 implementations of __vectorcall.





Using the /Gv compiler option(note:这个选项中后面的v代表了__vectorcall) causes each function in the module to compile as __vectorcall unless
the function is a member function, is declared with a conflicting calling convention attribute(note:这里指的是用另一种调用约定属性指定), uses a vararg variable argument list, or has the name main.(note:一些不能使用__vectorcall的情况,另外如果有变参列表,只能用__cdecl,另外对于main函数不能使用__fastcall、__thiscall、__vectorcall,可以使用的只有__cdecl、__stdcall)。

You can pass three kinds of arguments by register in __vectorcall functions: integer type values, vector type values, and homogeneous vector aggregate (HVA)
values.

An integer type satisfies two requirements: it fits in the native register size of the processor—for example, 4 bytes on an x86 machine or 8 bytes on an x64 machine—and it’s convertible to an integer of register length and back again without changing
its bit representation. For example, any type that can be promoted to int on x86 (long long on x64)—for example, a char or short—or that can be cast to int (long
long on x64) and back to its original type without change is an integer type. Integer types include pointer, reference, and struct or union types of 4 bytes (8 bytes on x64) or less. On x64 platforms, larger struct and union types
are passed by reference to memory allocated by the caller; on x86 platforms, they are passed by value on the stack.

A vector type is either a floating-point type—for example, a float or double—or an SIMD vector type—for example, __m128 or __m256.

注:Single Instruction Multiple Data,单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。

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

__clrcall 

(个人理解只有用到托管代码和c++代码相互调用时才需要关注,而且只在windows下有效)

指定只能从托管代码调用的函数。对所有只能从托管代码调用的虚函数使用 __clrcall。 但是,此调用约定不能用于从本机代码调用的函数。

当通过指针从托管函数调用到虚拟托管函数或从托管函数调用到托管函数时,可使用 __clrcall 来提高性能。

入口点是编译器生成的单独函数。 如果函数同时具有本机和托管入口点,则其中一个将是具有函数实现的实际函数。 其他函数将是调用到实际函数的单独函数(形式转换 (thunk))并允许公共语言运行时执行 PInvoke。 当将函数标记为 __clrcall 时,您可以指示函数实现必须是 MSIL,并且不生成本机入口点函数。

当采用本机函数的地址时,如果未指定 __clrcall,编译器将使用本机入口点。 __clrcall 指示函数为托管函数,并且不需要经历从托管到本机的转换。 在这种情况下,编译器将使用托管入口点。

当使用了 /clr(不是 /clr:pure 或 /clr:safe )而未使用 __clrcall 时,采用函数的地址将始终返回本机入口点函数的地址。 当使用了 __clrcall 时,不会创建本机入口点函数,因此您将获得托管函数的地址,而不是入口点形式转换 (thunk)
函数的地址。 有关详细信息,请参阅双重 Thunk (C++)

/clr(公共语言运行时编译) 表示所有函数和函数指针都是 __clrcall,且编译器不允许将编译单位中的函数标记为 __clrcall 之外的任何内容。 当使用 /clr:pure 时,__clrcall 只能在函数指针和外部声明上指定。

可以直接从使用 /clr 编译的现有 C++ 代码调用 __clrcall 函数(只要该函数具有 MSIL 实现)。 例如,__clrcall 函数无法直接从具有内联 asm 的函数调用,且无法调用特定于 CPU 的内部函数,即使这些函数是使用 /clr 编译的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: