解析__cdecl,__fastcall, __stdcall 的不同

解析__cdecl,__fastcall, __stdcall 的不同

在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式。
比如说__cdecl,它是标准的c方法的堆栈调用方式,就是在函数调用时的参数压入堆栈是与函数的声明顺序相反的,其它两个可以看MSDN,不过这个对我们编程没有太大的作用

调用约定

调用约定(Calling convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:


_cdecl

按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于"C"函数或者变量,修饰名是在函数名前加下划线。对于"C++"函数,有所不同。

如函数void test(void)的修饰名是_test;对于不属于一个类的"C++"全局函数,修饰名是?test@@ZAXXZ。

这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。


_stdcall

按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于"C"函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号"@"及参数的字节数,如函数int func(int a, double b)的修饰名是_func@12。对于"C++"函数,则有所不同。

所有的Win32 API函数都遵循该约定。


_fastcall

头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于"C"函数或者变量,修饰名以"@"为前缀,然后是函数名,接着是符号"@"及参数的字节数,如函数int func(int a, double b)的修饰名是@func@12。对于"C++"函数,有所不同。

未来的编译器可能使用不同的寄存器来存放参数。


  关键字   调用规则 参数传递方向 返回 参数寄存器 堆栈的清除  
  __cdecl   C调用规则 从右向左 EAX 无 调用者  
  __fastcall 寄存器 从左向右 EAX EAX、EBX、ECX 被调用者  
  __stdcall Win32标准 从右向左 EAX 无 被调用者  
  __pascal Pascal 从左向右 EAX 无 被调用者  
  __msfastcall Ms寄存器 从右向左 EAX/EDX ECX、EDX 被调用者   
   
C++   Builder中几种调用规则的比较  

 1.   名字分解:  
          没有名字分解的函数  
                  TestFunction1 //   __cdecl   calling   convention  
                  @TestFunction2 //   __fastcall   calling   convention  
                  TESTFUNCTION3 //   __pascal   calling   convention  
                  TestFunction4 //   __stdcall   calling   convention  
          有名字分解的函数  
                  @TestFunction1$QV //   __cdecl   calling   convention  
                  @TestFunction2$qv //   __fastcall   calling   convention  
                  TESTFUNCTION3$qqrv //   __apscal   calling   convention  
                  @TestFunction4$qqrv //   __stdcall   calling   convention  
          使用   extern   "C"   不会分解函数名  
   
          使用   Impdef   MyLib.def   MyLib.dll   生成   def   文件查看是否使用了名字分解  
   
  2.   调用约定:  
          __cdecl   缺省  
              是   Borland   C++   的缺省的   C   格式命名约定,它在标识符前加一下划线,以保留  
          它原来所有的全程标识符。参数按最右边参数优先的原则传递给栈,然后清栈。  
                  extaern   "C"   bool   __cdecl   TestFunction();  
              在   def   文件中显示为    
                  TestFunction @1  
              注释:   @1   表示函数的顺序数,将在“使用别名”时使用。  
   
          __pascal   Pascal格式  
              这时函数名全部变成大写,第一个参数先压栈,然后清栈。  
                  TESTFUNCTION @1 //def   file  
   
          __stdcall   标准调用  
              最后一个参数先压栈,然后清栈。  
                  TestFunction @1 //def   file  
   
          __fastcall   把参数传递给寄存器  
              第一个参数先压栈,然后清栈。  
                  @TestFunction @1 //def   file  
   
  3.   解决调用约定:  
              Microsoft   与   Borland   的   __stdcall   之间的区别是命名方式。   Borland   采用  
          __stdcall   的方式去掉了名字起前的下划线。   Microsoft   则是在前加上下划线,在  
          后加上   @   ,再后跟为栈保留的字节数。字节数取决于参数在栈所占的空间。每一个  
          参数都舍入为   4   的倍数加起来。这种   Miocrosoft   的   Dll   与系统的   Dll   不一样。  
   
  4.   使用别名:  
              使用别名的目的是使调用文件   .OBJ   与   DLL   的   .DEF   文件相匹配。如果还没有  
          .DEF   文件,就应该先建一个。然后把   DEF   文件加入   Project。使用别名应不断  
          修改外部错误,如果没有,还需要将   IMPORTS   部分加入   DEF   文件。  
                  IMPORTS  
                  TESTFUNCTIOM4   =   dllprj.TestFunction4  
                  TESTFUNCTIOM5   =   dllprj.WEP @500  
                  TESTFUNCTIOM6   =   dllprj.GETHOSTBYADDR @51  
              这里需要说明的是,调用应用程序的   .OBJ   名与   DLL   的   .DEF   文件名是等价的,  
          而且总是这样。甚至不用考虑调用约定,它会自动匹配。在前面的例子中,函数被  
          说明为   __pascal,因此产生了大写函数名。这样链接程序不会出错。

其他连接

http://blog.csdn.net/lotomer/archive/2006/06/28/844658.aspx

你可能感兴趣的:(解析__cdecl,__fastcall, __stdcall 的不同)