函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的。它决定以下内容:
常见的函数定义
int function(int a, int b);
这种书写方式,没有显式的说明调用约定,编译器会默人我们使用__cdecl
约定。
另一种函数定义方式
int __stdecall function(int a, int b);
这种定义方式显式的指出了函数调用约定的规则是__stdcall
接下来,具体分析一下这几种调用规则的含义:
常见的调用约定有__cdecl、__stdcall、fastcall
__cdecl 是 C Declaration 的缩写,表示 C 和 C++ 默认的函数调用约定。是C/C++和MFCX的默认调用约定。
__stdcall是Standard Call的缩写,是C++的标准调用方式,当然这是微软定义的标准,__stdcall通常用于Win32 API中(可查看WINAPI的定义)。
__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的。
__thiscall是C++类成员函数缺省的调用约定,但它没有显示的声明形式。因为在C++类中,成员函数调用还有一个this指针参数,因此必须特殊处理,thiscall调用约定的特点:
几种调用约定的比较
项目 | __cdecl | __stdcall | __fastcall |
---|---|---|---|
参数传递方式 | 右->左 | 右->左 | 左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数自右向左压栈传送 |
清理栈方 | 调用者清理 | 被调用函数清理 | 被调用函数清理 |
适用场合 | C/C++、MFC的默认方式; 可变参数的时候使用; | Win API | 要求速度快,适用于对性能要求较高的场合。 |
C编译修饰约定 | _functionname | _functionname@number | @functionname@number |
https://blog.csdn.net/luoweifu/article/details/52425733
__declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。
__declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。
.def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件。.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。
声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。一般用于dll中
省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类
__declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。
声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中
不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
关键字 | 含义 | |
---|---|---|
dllexport | 将当前动态库的接口导出,供别的程序使用 | |
dllimport | 将别的动态库的接口导入到当前程序,供当前使用 |
#define LIBSCRENDER_API __declspec (dllexport)
LIBSCRENDER_API int __stdcall sum(int a, int b)
{
return a + b;
}
https://docs.microsoft.com/zh-cn/cpp/cpp/dllexport-dllimport?view=vs-2019
持续更新……