zoukankan      html  css  js  c++  java
  • dll函数生成规则

    【转】http://blog.csdn.net/beanjoy/article/details/9136127

    所谓名字修饰约定,就是指变量名、函数名等经过编译后重新输出名称的规则。

      比如源代码中函数名称为int Func(int a,int b),经过编译后名称可能为?Func@@YAHHH@Z、?Func@@YGHHH@Z、_Func@8,也有可能与源代码中名称相同为Func。

      影响编译后输出的名称通常与名字修饰约定(extern "C"、extern "C++"等)和函数调用约定(__stdcall、__cdecl等)等相关。

      口说千遍,不如实际演练一遍。那么,就让我们写代码来测试下。

      注意,本文只讨论extern "C"、extern "C++"和__stdcall、__cdecl相关的约定,其他约定不在本文讨论范围内。另外,编译的环境为XP + VC++6.0SP6。

      首先,用C方式导出两个函数:

      Dll1.c

    [cpp] view plaincopy
     
    1. _declspec(dllexport) int __cdecl Func_cdecl(int a,int b)  
    2. {  
    3.     return 1;  
    4. }  
    5.   
    6. _declspec(dllexport) int __stdcall Func_stdcall(int a,int b)  
    7. {  
    8.     return 1;  
    9. }  

      导出的两个函数名为:


      再以C++方式导出:

      Dll1.cpp

    [cpp] view plaincopy
     
    1. _declspec(dllexport) int __cdecl Func_cdecl(int a,int b)  
    2. {  
    3.     return 1;  
    4. }  
    5.   
    6. _declspec(dllexport) int __stdcall Func_stdcall(int a,int b)  
    7. {  
    8.     return 1;  
    9. }  

      导出结果如下:

      然后,我们再以C++方式导出如下代码中的函数:

    [cpp] view plaincopy
     
    1. extern "C" _declspec(dllexport) int __stdcall Func_C_stdcall(int a,int b)  
    2. {  
    3.     return 1;  
    4. }  
    5.   
    6. extern "C++" _declspec(dllexport) int __stdcall Func_CPP_stdcall(int a,int b)  
    7. {  
    8.     return 1;  
    9. }  
    10.   
    11. extern "C" _declspec(dllexport) int __cdecl Func_C_cdecl(int a,int b)  
    12. {  
    13.     return 1;  
    14. }  
    15.   
    16. extern "C++" _declspec(dllexport) int __cdecl Func_CPP_cdecl(int a,int b)  
    17. {  
    18.     return 1;  
    19. }  

      导出结果如下:

      有了以上实验结果,我们再结合以下名字输出规则进行理解:

    1. C方式编译(extern "C"):
      1. __stdcall调用约定:输出名称在原名称前加一下划线,后面再加上一个“@”和其参数的总字节数(_原名称@参数总字节数),如名称int Func_C_stdcall(int a,int b)输出为_Func_C_stdcall@8;
      2. __cdecl调用约定:与原名称相同,如名称int Func_C_cdecl(int a,int b)输出还是为Func_C_cdecl;
    2. C++方式编译(extern "C++"):
      1. __stdcall调用约定:
        1. 输出名称以“?”开始,后跟原名称;
        2. 原名称后再跟“@@YG”,后面再跟返回值代号和参数表代号,代号表示如下:
          X--void ,
          D--char,
          E--unsigned char,
          F--short,
          H--int,
          I--unsigned int,
          J--long,
          K--unsigned long,
          M--float,
          N--double,
          _N--bool,
          ...
          PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;
        3. 参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。如名称int Func_CPP_stdcall(int a,int b)编译后的输出名称为?Func_CPP_stdcall@@YGHHH@Z。
      2. __cdecl调用约定:与_stdcall调用约定基本一致,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。如名称int Func_CPP_cdecl(int a,int b)编译后输出名称为?Func_CPP_cdecl@@YAHHH@Z。

      

      有个这个规则,再回头去看我们的实验结果,就很好理解了。

      当然,编译C文件和编译CPP文件,不需加extern "C"和extern "C++",因为编译C文件当然默认的是extern "C",而编译CPP文件则默认的是extern "C++"。

      现在我们也能理解为什么导出DLL时通常需要加上extern "C"。试想,如果一个C++导出的dll,没有加extern "C",则导出的名称为extern "C++"约定下的名称。如果这个dll需要提供给用C编写的程序使用,那么这个程序是无法调用这个dll的,因为C写的程序遵循的是extern "C"约定,链接时链接器将按照extern "C"约定的名称去寻找外部名称,这当然找不到,因为dll中的输出名称为extern "C++"约定下的名称。

  • 相关阅读:
    vue 回到顶部效果实现
    C# rabbitmq 安装步骤以及使用方法
    ​Everspin MRAM常见问题解答
    带有ECC的异步SRAM存储器适用于各种应用
    ​MCU是控制电子产品的大脑
    全球MCU市场供需失衡情况愈发严重
    如何选择非易失性SRAM
    SRAM VS DRAM
    SoC上的内存
    EEPROM和FLASH在大多数应用场合中无法替代SRAM
  • 原文地址:https://www.cnblogs.com/candl/p/4371837.html
Copyright © 2011-2022 走看看