zoukankan      html  css  js  c++  java
  • [转载]visual studio的C/C++修饰名及调用约定(如__cdecl)

    程序出链接错误的时候,经常看到lnk errorxxx:某某函数、某某变量找不到等等,里面的函数名通常都很难看明白,因为使用的是修饰名。

    C 和 C++ 程序中的函数在内部通过其修饰名加以识别。修饰名是在编译函数定义或函数原型期间由编译器创建的字符串。

    既然是编译器创建的字符串,不同的编译器使用的修饰名都不一样,这里讲的是visual studio的

    C 函数的修饰形式取决于其声明中使用的调用约定。有三种调用约定,如下所示。

    调用约定

    修饰

    __cdecl(默认值)

    前导下划线 (_)

    __stdcall

    前导下划线 (_) 和结尾 at 符 (@),后面跟表示参数列表中的字节数的数字

    __fastcall

    __stdcall 相同,但前置符不是下划线,而是 @ 符

    下面讲的修饰名、函数名变化规则和举得例子都是C语言的,C++的因为要考虑到重载所以更复杂,参见我的博文extern C

    __cdecl是C/C++函数的默认调用约定。其堆栈由调用该函数的调用者(caller)来负责清理,因此该函数不需要知道自己有多少个参数,可以拥有可变参数(是vararg function)。也正因为如此,caller里面要有清理堆栈的代码(这里不是指程序员写的C/C++代码,而是编译器编译链接时要加上的二进制代码),所以以__cdecl方式调用子函数比用__stdcall方式调用,生成的可执行文件要更大一些。

    参数传递顺序     从右到左

    堆栈清理         调用者将参数弹出栈

    修饰名           函数名前有下划线_,除非以C链接方式输出__cdecl函数

    函数名           函数名没有变化

    一个类的非静态的函数成员,如果函数定义在类体外,那么在类体外的函数定义不用加上调用约定。例如:

    有一个类定义如下:

    struct CMyClass {
       void __cdecl mymethod();
    };

    则类体外的定义

    void CMyClass::mymethod() { return; }和void __cdecl CMyClass::mymethod() { return; }是等同的。

    __stdcall,所有的windowsAPI都以__stdcall方式调用。因为windows.h里有定义:

    #define WINAPI __stdcall

    __stdcall方式调用函数的时候,由被调用函数(callee)清理堆栈,所以参数个数必须确定。

    参数传递顺序     从右到左

    堆栈清理         被调用者将参数弹出栈

    参数传递         传值的拷贝,除非参数是指针或者引用

    修饰名           函数名前有下划线_,函数名后面跟@,然后是所有参数的字节数(十进制)

    函数名           函数名没有变化

    因此函数int func( int a, double b )编译后的修饰名是_func@12  (int4字节,double8字节,共12字节)

    一个类的非静态的函数成员,如果函数定义在类体外,那么在类体外的函数定义不用加上调用约定。例如:

    有一个类定义如下:

    struct CMyClass {
       void __stdcall mymethod();
    };

    则类体外的定义

    void CMyClass::mymethod() { return; }和void __stdcall CMyClass::mymethod() { return; }是等同的。

    __fastcall指的是如果可能,通过寄存器传递参数(所以叫做fast call)

    参数传递顺序     前两个DWORD(unsigned long,32bit)或者更小尺寸的参数

                     (The first two DWORD or smaller arguments )通过ECX和EDX寄存器传递,

                     其余的从右到左

    堆栈清理         调用者将参数弹出栈(所以参数个数也必须确定)

    修饰名           函数名前有@,函数名后跟@,然后是参数的总字节数

    函数名           函数名没有变化

    因此函数int func( int a, double b )编译后的修饰名是 @func@12  (int4字节,double8字节,共12字节)

    关于参数传递顺序,我的理解是:int func(DWORD i,DWORD j,int x,int y)当然是i、j分别用ECX和EDX传递,x、y从右到左通过栈传递。

    int func(DWORDLONG i,DWORDLONG j,int x,int y),是x、y分别用ECX和EDX传递,64位的i和j从右到左通过栈传递

     

    一个类的非静态的函数成员,如果函数定义在类体外,那么在类体外的函数定义不用加上调用约定。例如:

    有一个类定义如下:

    struct CMyClass {
       void __fastcall mymethod();
    };

    则类体外的定义

    void CMyClass::mymethod() { return; }和void __fastcall CMyClass::mymethod() { return; }是等同的。

    MSDN里给了调用约定的使用例子

    // Example of the __cdecl keyword on function
    int __cdecl system(const char *);
    // Example of the __cdecl keyword on function pointer
    typedef BOOL (__cdecl *funcname_ptr)(void * arg1, const char * arg2, DWORD flags, ...);

    比较有意思的是 function pointer,我承认之前自己没有function pointer这个概念。特意到网上搜了下,转了篇博文Function Pointer(C)和Function Object(C++)

    MSDN里还特别说明了,对于安腾处理器以及X64处理器,调用约定__cdecl和__stdcall被忽略。

    对于安腾处理器以及AMD64(这里不是X64,所以不包括Intel-64)处理器,调用约定__fastcall被忽略。

    在安腾处理器中,上述三种调用约定的参数全部通过寄存器传递。(不过对我们普通开发者没什么意义,与我何干)

    visual studio里设置默认调用约定:项目属性-》C/C++ -》高级-》调用约定

    建立新项目的时候默认调用约定是__cdecl。

  • 相关阅读:
    Git操作命令2-在Git仓库里管理文件历史-分支操作
    mvvmlight框架搭建VS版本不同导致的问题
    wpf命令详解
    wpf触发器
    wpf控件模型
    wpf中Interaction.Behaviors详解
    wpf附加属性详解
    wpf依赖属性概述
    wpf体系结构
    MySql5.7下载安装配置教程
  • 原文地址:https://www.cnblogs.com/loongfee/p/2252149.html
Copyright © 2011-2022 走看看