__cdecl、__stdcall、__fastcall

__cdecl、__stdcall、__fastcall

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_pureDll
PureDll.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
                  _nPureDll
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   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  nPureDll
PureDll.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
                  _nPureDll
PureDll.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@@A
PureDll.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@@A
PureDll.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、__fastcall)