1.这三个修饰符的基本意思
__cdecl:C调用方式,VC默认使用该方式,参数从右向左传递,参数个数可变,栈的初始和清理由调用者完成
__stdcall:标准调用方式,多种语言使用这种调用方式,参数从右向左传递,参数个数不可变,栈的初始和清理由被调用者完成
__fastcall:参数尽量使用寄存器传递,很少用到,这里不予讨论。
2.这三个修饰符的使用位置
这三个修饰符要用在类型名之后,不能用在类型名之前,否则语法无法识别,容易被编译器误以为是默认int错误。
这三个修饰符都不能用于变量,如果使用会有警告,如下:
编译器警告(等级 1)C4229
对数据声明使用 Microsoft 修饰符(如 __cdecl)是已过时的做法。 这三个函数只能用在函数前面,不能用于别的东西上,可以用在成员函数上,但是这个不像__declspec(dllimport)一样,__declspec(dllimport)可以用在类声明的前面,这样整个类都被导出,包括成员变量和函数,而这几个调用修饰符只能直接用在类声明体中的成员函数的前面(而且函数定义前也需要),来说明成员函数的调用方式。下表是产生规则:
C 和C++ 对应不同的调用约定,产生的修饰符也各不相同,对于函数test(void)如下:
调用约定 extern "C" 或 .c 文件 .cpp、.cxx 或 /TP
C 命名约定 (__cdecl)
_test
?test@@ZAXXZ
Fastcall 命名约定 (__fastcall)
@test@0
?test@@YIXXZ
标准调用命名约定 (__stdcall)
_test@0
?test@@YGXXZ
第二列,也就是C风格的情况下,@后面的数字表述所有参数的字节数之和。
第三列,也就是CPP风格的情况下,修饰符有些复杂,以后补充吧。
以前面的一片随笔PureDllApp为例,VC9.0进行分析:
1、extern "C"作用下:
(1)__cdecl作用下(由于VC默认就是这种调用方式,所以这里就不给出代码,和前面的一片PureDllApp的代码一样):
PureApp.exe对PureDll.dll的导入修饰符如下:
4 fnPureDll
6 nPureDll
1 ??1CPureDll@@QAE@XZ
3 ?setValue@CPureDll@@QAEXH@Z
0 ??0CPureDll@@QAE@H@Z
5 g_pureDllPureDll.lib的导出修饰符如下:
??0CPureDll@@QAE@H@Z (public: __thiscall CPureDll::CPureDll(int))
??1CPureDll@@QAE@XZ (public: __thiscall CPureDll::~CPureDll(void))
??4CPureDll@@QAEAAV0@ABV0@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))
?setValue@CPureDll@@QAEXH@Z (public: void __thiscall CPureDll::setValue(int))
_fnPureDll
_g_pureDll
_nPureDllPureDll.dll的导出修饰符如下:
ordinal hint RVA name
1 0 00011005 ??0CPureDll@@QAE@H@Z = @ILT+0(??0CPureDll@@QAE@H@Z)
2 1 000110B9 ??1CPureDll@@QAE@XZ = @ILT+180(??1CPureDll@@QAE@XZ)
3 2 000110C3 ??4CPureDll@@QAEAAV0@ABV0@@Z = @ILT+190(??4CPureDll@@QAEAAV0@ABV0@@Z)
4 3 00011168 ?setValue@CPureDll@@QAEXH@Z = @ILT+355(?setValue@CPureDll@@QAEXH@Z)
5 4 000110EB fnPureDll = @ILT+230(_fnPureDll)
6 5 00019138 g_pureDll = _g_pureDll
7 6 00019000 nPureDll = _nPureDll(2)__stdcall作用下(调用方式的改变在函数声明和定义之前都要使用,下面先是发生改变的头文件,cpp文件中在函数定义前做一样的修改即可):
需要注意的就是调用修饰符只能用在函数和成员函数的前面,而且要在返回值类型之后的位置。
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 PUREDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// PUREDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef PUREDLL_EXPORTS
#define PUREDLL_API __declspec(dllexport)
#else
#define PUREDLL_API __declspec(dllimport)
#endif
// 此类是从 PureDll.dll 导出的
class PUREDLL_API CPureDll {
public:
__stdcall CPureDll(int num);
// TODO: 在此添加您的方法。
__stdcall ~CPureDll(void);
void __stdcall setValue(int value);
private:
int m_num;
int *m_p;
};
extern PUREDLL_API int nPureDll;
extern PUREDLL_API CPureDll g_pureDll;
PUREDLL_API int __stdcall fnPureDll(void);
#ifdef __cplusplus
}
#endif
PureApp.exe对PureDll.dll的导入修饰符如下:
5 g_pureDll
1 ??1CPureDll@@QAE@XZ
4 _fnPureDll@0
3 ?setValue@CPureDll@@QAGXH@Z
0 ??0CPureDll@@QAE@H@Z
6 nPureDllPureDll.lib的导出修饰符如下:
??0CPureDll@@QAE@H@Z (public: __thiscall CPureDll::CPureDll(int))
??1CPureDll@@QAE@XZ (public: __thiscall CPureDll::~CPureDll(void))
??4CPureDll@@QAEAAV0@ABV0@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))
?setValue@CPureDll@@QAGXH@Z (public: void __stdcall CPureDll::setValue(int))
_fnPureDll@0
_g_pureDll
_nPureDllPureDll.dll的导出修饰符如下:
ordinal hint RVA name
1 0 00011005 ??0CPureDll@@QAE@H@Z = @ILT+0(??0CPureDll@@QAE@H@Z)
2 1 000110C3 ??1CPureDll@@QAE@XZ = @ILT+190(??1CPureDll@@QAE@XZ)
3 2 000110CD ??4CPureDll@@QAEAAV0@ABV0@@Z = @ILT+200(??4CPureDll@@QAEAAV0@ABV0@@Z)
4 3 0001100F ?setValue@CPureDll@@QAGXH@Z = @ILT+10(?setValue@CPureDll@@QAGXH@Z)
5 4 0001105A _fnPureDll@0 = @ILT+85(_fnPureDll@0)
6 5 00019138 g_pureDll = _g_pureDll
7 6 00019000 nPureDll = _nPureDll
比较上面的两组之间的差别,就可以发现__cdecl和__stdcall之间的差别。
第一点,构造函数在这两种调用方式中的产生的修饰符相同,其实产生的修饰符不是这两种的任何一种,而是__thiscall的修饰符,从PureDll.lib的后面括号中可以看出来,而且VC9.0中在构造函数前面使用__cdecl会直接产生编译时警告c4166,如下:
编译器警告(等级 1)C4166
构造函数/析构函数的非法调用约定
构造函数和析构函数不能有非平台默认的调用约定(显式指定 __clrcall 时除外)。
再对照第一组中setValue函数在PureDll.lib中产生的修饰符,可以知道类成员函数默认使用__thiscall修饰符,即使编译器选项中选择了__cdecl,也就是说构造函数和析构函数必须使用当前平台默认的调用方式----__thiscall,其它的成员函数可以通过在函数名前(声明和定义)强制加上指定的调用方式即可,编译器选项中的调用方式不行(这里指定的方式只是对非成员函数有效的,成员函数会默认使用__thiscall)。
其实__thiscall和__stdcall差别不大,都是被调用者控制栈的初始和清理,只不过前者的this指针放在寄存器中,而后者的放在栈里。所以对于成员函数一般不要指定调用方式,使用默认的__shicall即可,除非要做参数数量可变的成员函数,不过这种函数尽量避免。
最后补充上setValue函数的__cdecl调用方式产生的修饰符:
?setValue@CPureDll@@QAAXH@Z (public: void __cdecl CPureDll::setValue(int))
2.对应上面的extern "C",下面是cpp(去掉extern "C")下对应上面的修饰符:
(1)__cdecl作用下:
PureApp.exe对PureDll.dll的导入修饰符如下:
3 ?fnPureDll@@YAHXZ
5 ?nPureDll@@3HA
1 ??1CPureDll@@QAE@XZ
6 ?setValue@CPureDll@@QAEXH@Z
0 ??0CPureDll@@QAE@H@Z
4 ?g_pureDll@@3VCPureDll@@APureDll.lib的导出修饰符如下:
??0CPureDll@@QAE@H@Z (public: __thiscall CPureDll::CPureDll(int))
??1CPureDll@@QAE@XZ (public: __thiscall CPureDll::~CPureDll(void))
??4CPureDll@@QAEAAV0@ABV0@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))
?fnPureDll@@YAHXZ (int __cdecl fnPureDll(void))
?g_pureDll@@3VCPureDll@@A (class CPureDll g_pureDll)
?nPureDll@@3HA (int nPureDll)
?setValue@CPureDll@@QAEXH@Z (public: void __thiscall CPureDll::setValue(int))PureDll.dll的导出修饰符如下:
ordinal hint RVA name
1 0 00011005 ??0CPureDll@@QAE@H@Z = @ILT+0(??0CPureDll@@QAE@H@Z)
2 1 000110B9 ??1CPureDll@@QAE@XZ = @ILT+180(??1CPureDll@@QAE@XZ)
3 2 000110C3 ??4CPureDll@@QAEAAV0@ABV0@@Z = @ILT+190(??4CPureDll@@QAEAAV0@ABV0@@Z)
4 3 00011113 ?fnPureDll@@YAHXZ = @ILT+270(?fnPureDll@@YAHXZ)
5 4 00019138 ?g_pureDll@@3VCPureDll@@A = ?g_pureDll@@3VCPureDll@@A(class CPureDll g_pureDll)
6 5 00019000 ?nPureDll@@3HA = ?nPureDll@@3HA (int nPureDll)
7 6 00011168 ?setValue@CPureDll@@QAEXH@Z = @ILT+355(?setValue@CPureDll@@QAEXH@Z)(2)__stdcall作用下:
PureApp.exe对PureDll.dll的导入修饰符如下: 3 ?fnPureDll@@YGHXZ
5 ?nPureDll@@3HA
1 ??1CPureDll@@QAE@XZ
6 ?setValue@CPureDll@@QAGXH@Z
0 ??0CPureDll@@QAE@H@Z
4 ?g_pureDll@@3VCPureDll@@APureDll.lib的导出修饰符如下:
??0CPureDll@@QAE@H@Z (public: __thiscall CPureDll::CPureDll(int))
??1CPureDll@@QAE@XZ (public: __thiscall CPureDll::~CPureDll(void))
??4CPureDll@@QAEAAV0@ABV0@@Z (public: class CPureDll & __thiscall CPureDll::operator=(class CPureDll const &))
?fnPureDll@@YGHXZ (int __stdcall fnPureDll(void))
?g_pureDll@@3VCPureDll@@A (class CPureDll g_pureDll)
?nPureDll@@3HA (int nPureDll)
?setValue@CPureDll@@QAGXH@Z (public: void __stdcall CPureDll::setValue(int))PureDll.dll的导出修饰符如下:
ordinal hint RVA name
1 0 00011005 ??0CPureDll@@QAE@H@Z = @ILT+0(??0CPureDll@@QAE@H@Z)
2 1 000110BE ??1CPureDll@@QAE@XZ = @ILT+185(??1CPureDll@@QAE@XZ)
3 2 000110C8 ??4CPureDll@@QAEAAV0@ABV0@@Z = @ILT+195(??4CPureDll@@QAEAAV0@ABV0@@Z)
4 3 00011118 ?fnPureDll@@YGHXZ = @ILT+275(?fnPureDll@@YGHXZ)
5 4 00019138 ?g_pureDll@@3VCPureDll@@A = ?g_pureDll@@3VCPureDll@@A(class CPureDll g_pureDll)
6 5 00019000 ?nPureDll@@3HA = ?nPureDll@@3HA (int nPureDll)
7 6 0001100F ?setValue@CPureDll@@QAGXH@Z = @ILT+10(?setValue@CPureDll@@QAGXH@Z)
补充一个强制指定__cdecl的setValue的修饰符:
?setValue@CPureDll@@QAAXH@Z (public: void __cdecl CPureDll::setValue(int))
cpp的__stdcall也没有什么特别的,和extern "C"相比,没有什么大的变化,唯一变得就是后面的字母变多了。而且能够发现无论是否存在extern "C",成员函数的修饰符都没有改变,这是因为C语言本身没有成员函数,所以extern "C对成员函数是无效的。
所以成员函数有两个特殊点:首先是extern "C"对类无效,即成员函数无效,其次是成员函数默认是__thiscall,除非在函数前强制指明其他调用方式。
暂时到这里,以后有机会再补充别的。
分享到:
相关推荐
- **返回值**:与 __cdecl 和 __stdcall 类似,返回值通常存储在 EAX 寄存器中。 - **修饰名格式**:函数名修饰为 `@functionname@number` 形式。 #### 应用场景: - **性能敏感的代码**:当函数调用频繁且对性能有...
_stdcall、_cdecl和_fastcall 的区别.zip
__stdcall、__cdecl和__fastcall各有优势和应用场景,选择合适的调用约定对于优化代码性能和提高程序的可维护性至关重要。了解这些调用约定的具体细节有助于开发者更好地理解底层机制,并能够根据项目需求做出最佳...
本文将深入探讨三种常见的函数调用约定:`__stdcall`、`__cdecl` 和 `__fastcall`,并对比它们的特点和适用场景。 首先,`__cdecl` 是C语言默认的调用约定,也是大多数编译器默认使用的约定。在`__cdecl`中,函数的...
根据给定的文件信息,我们可以深入探讨C/C++中几种主要的函数调用方式:__cdecl、__stdcall和__fastcall,以及C++特有的thiscall。 ### __cdecl `__cdecl`,即“C声明”或“C default”,是C/C++中默认的调用约定...
本文档详细介绍了C++builder调用VC编写的DLL的过程,并对__cdecl、__fastcall、__stdcall三种调用约定的解析。 在函数调用过程中,堆栈的使用是非常重要的。C++builder和VC在调用DLL时,使用了不同的调用约定,导致...
### stdcall 与 cdecl:函数调用方式详解 在编程领域,特别是在C和C++语言中,函数调用约定(Calling Convention)是一个重要的概念。它规定了如何传递参数、谁来清理栈空间以及如何处理返回值等细节。本文将详细...
- **fastcall**:前两个32位或64位的参数分别存储在ECX和EDX寄存器中,其余参数从右到左压栈,由调用者清理栈空间。 - **thiscall**:用于C++成员函数,第一个参数(即`this`指针)存储在ECX寄存器中,其余参数从右...
本文将详细介绍Windows系统下的三种主要函数调用约定:`__stdcall`、`__cdecl`以及`PASCAL`(在某些情况下也被称为`__fastcall`)。通过对比这些调用约定的特点及其对程序的影响,帮助读者更好地理解编译原理中的...
C++调用约定是指在C++中函数调用的多种形式,它们是:_stdcall、_cdecl、_fastcall、thiscall和naked call。每种调用约定都有其特点和应用场景。 _stdcall调用约定 _stdcall调用约定是Pascal程序的缺省调用方式,...
在提供的文件中,提及了四种调用约定:__cdecl、__stdcall、__thiscall 和 __fastcall。下面将对这些调用约定进行详细介绍。 1. __cdecl 调用约定 __cdecl 是 C 和 C++ 默认的函数调用约定。在 __cdecl 约定下,...
非常好用的thunk,全面兼容32位和64位,msvc的各种调用方式,如__stdcall,__fastcall,_cdecl。例如: call_thunk::thunk<AFX_THREADPROC> thunk; void CMyClass::start() { thunk.bind(*this, &CMyClass::Run); ...
本文将深入探讨VC++中几种常见的调用约定:`__cdecl`、`__stdcall`、`__fastcall`和`__pascal`,以及它们在实际开发中的应用。 #### 1\. `__cdecl` `__cdecl`(C默认调用约定)是最常用的调用约定之一。在`__cdecl...
本文将深入探讨Visual C++中的几种主要函数调用约定:`__cdecl`、`__stdcall`、`__fastcall`、`__thiscall` 和 `naked call`,并分析它们的特点、应用场景以及可能带来的影响。 #### 1. `__cdecl` `__cdecl` 是C/...
本文将详细介绍四种常见的函数调用约定:`_cdecl`、`_stdcall`、`_fastcall` 和 `_thiscall`。 ### 1. _cdecl (C Declaration) `_cdecl` 是 C 和 C++ 缺省的调用约定。在此约定下,参数是从右到左压入栈中的,并且...
在Visual C/C++编译器中,有四种主要的函数调用约定:__cdecl、__stdcall、__fastcall和thiscall。了解这些约定的差异对于编写高效且兼容的代码至关重要。 首先,__cdecl是C和C++的默认调用约定。在这种约定中,...
- **__cdecl**:与_stdcall类似,但参数的清理工作由调用者负责。这种调用约定适用于大多数C/C++函数。 - **__fastcall**:参数传递方面,前两个整型或浮点型参数通过寄存器(通常是ECX和EDX)传递,其余参数按_...
例如,尽管函数声明中使用了不同的调用约定,如`__cdecl`和`__stdcall`,但只要我们知道参数是如何压栈和返回值是如何处理的,就可以通过类型转换来间接调用。例如,在上述代码示例中,通过typedef定义一个新的函数...