【问题标题】:questions about name mangling in C++关于 C++ 中名称修饰的问题
【发布时间】:2011-02-25 14:35:00
【问题描述】:

我正在尝试学习和理解 C++ 中的名称修饰。以下是一些问题:

(1) 来自devx

当一个全局函数被重载时,每个重载版本生成的重载名称都是唯一的。名称修饰也适用于变量。因此,具有相同用户给定名称的局部变量和全局变量仍然会得到不同的重命名。

除了重载函数和同名全局和局部变量之外,还有其他使用名称修饰的示例吗?

(2) 来自Wiki

当语言允许不同的实体使用相同的标识符命名时,只要它们占用不同的命名空间(其中命名空间通常由模块、类或显式命名空间指令定义),就会出现这种需求。

我不太明白为什么名称修饰只适用于标识符属于不同命名空间的情况,因为重载函数可以在同一个命名空间中,同名的全局变量和局部变量也可以在同一个空间中.这个怎么理解?

同名但在不同范围内的变量是否也使用名称修饰?

(3) C 有名称修饰吗?如果不是,那如何处理一些全局变量和局部变量同名的情况呢? C 没有重载函数,对吧?

感谢和问候!

【问题讨论】:

    标签: c++ c compilation linker name-mangling


    【解决方案1】:

    C 不进行名称修改,尽管它会在函数名称前添加下划线,因此 printf(3) 实际上是 libc 对象中的 _printf

    在 C++ 中,情况有所不同。它的历史是最初 Stroustrup 创建了“C with classes”或cfront,这是一个将早期 C++ 转换为 C 的编译器。然后剩下的工具 - C 编译器和链接器将用于生成目标代码。这意味着必须以某种方式将 C++ 名称转换为 C 名称。这正是name mangling 所做的。它为每个类成员和全局/命名空间函数和变量提供一个唯一的名称,因此命名空间和类名(用于解析)和参数类型(用于重载)以某种方式包含在最终的链接器名称中。

    使用nm(1) 之类的工具很容易看到这一点 - 编译您的 C++ 源代码并查看生成的符号。以下是在 OSX 上使用 GCC:

    namespace zoom
    {
        void boom( const std::string& s )
        {
            throw std::runtime_error( s );
        }
    }
    
    ~$ nm a.out | grep boom
    0000000100001873 T __ZN4zoom4boomERKSs
    

    在 C 和 C++ 中,局部(自动)变量不产生符号,而是存在于寄存器或堆栈中。

    编辑:

    局部变量在生成的目标文件中没有名称,仅仅是因为链接器不需要知道它们。所以没有名字,没有修改。其他所有内容(链接器必须查看)都在 C++ 中进行了名称修改。

    【讨论】:

    • 如果你有嵌套的模板实例,这会更有趣:-)
    • 是的,我通常称它为 pain 虽然... :)
    • 谢谢尼古拉!正如您所说的“在 C 和 C++ 中,局部(自动)变量不产生符号,但存在于寄存器或堆栈中”,局部变量的名称是否在 C++ 中被破坏?如果没有,哪些类型的变量名称会被破坏?
    • C 中的前导下划线只出现在少数几个奇怪的平台上,这与一般规则相去甚远。
    【解决方案2】:

    Mangling 只是编译器让链接器满意的方式。

    在 C 中,无论如何都不能有两个同名的函数。所以这就是链接器被编写的假设:唯一名称。 (您可以在不同的编译单元中拥有静态函数,因为链接器不关心它们的名称。)

    在 C++ 中,您可以拥有两个同名的函数,只要它们具有不同的参数类型。因此,C++ 以某种方式将函数名与类型结合。这样,链接器将它们视为具有不同的名称。

    修改的确切方式对程序员来说并不重要,只有编译器才有意义,实际上每个编译器的处理方式都不同。重要的是每个具有相同基本名称的函数都以某种方式对链接器来说是唯一的。

    您现在可以看到,将命名空间和模板添加到混合中会不断扩展这一原则。

    【讨论】:

    • “请注意,名称如何被破坏并不重要”。它确实很重要 - 例如,当您链接由不同编译器编译的库时。这就是为什么许多 C++ 库以不同版本发布的原因,一个用于 MSVC,一个用于 mingw/gcc 等。
    • 我的意思是:确切的修改方式对程序员来说并不重要。
    • 不破坏与当前 API 的向后兼容性非常重要,可能是向函数添加 默认参数。主要问题是,新函数是否有不同的名称?从我在这里阅读的内容来看,答案很可能是 YES :-(
    【解决方案3】:

    从技术上讲,它是“装饰”。这听起来不那么粗鲁,但也有点混乱,这意味着CreditInterest 可能会重新排列成IntCrederestit,而实际发生的情况更像_CreditInterest@4,公平地说,“装饰”多于混乱。也就是说,我也称它为 mangling :-) 但如果您搜索“C++ 名称修饰”,您会发现更多技术信息和示例。

    【讨论】:

    • 它实际上取决于编译器。其中一些从字面上将名称分解为除了编译器之外毫无意义的字符串。旧版本的 VC++ 在这方面尤其糟糕。 :) 但是,是的,两个搜索词都是有效的。
    • 我同意;当我开始时,我们只说修饰,并且在过去几十年的某个时候,装饰变得更加普遍,当我开始查看那些被修饰的名字时,装饰似乎确实符合要求。我的猜测是有人改变了他们做事的方式,也想把旧名字抛在脑后。虽然只是部分成功:-)
    • 嗯...搜索“C++ 名称装饰”的大多数结果实际上都会显示带有“mangling”的标题:-)。看起来谷歌在该上下文中将“装饰”散列到与“修饰”相同的插槽中。
    【解决方案4】:

    除了重载函数和同名全局和局部变量之外,还有其他使用名称修饰的示例吗?

    C++ 总是会破坏 所有 符号。这对编译器来说更容易。通常,重整对参数列表或类型进行编码,因为这些是需要重整的最常见原因。

    C 不会破坏。作用域用于控制对同名的局部和全局变量的访问。

    【讨论】:

    • 谢谢唐尼。您是否认为名称修饰仅适用于具有相同名称但位于不同命名空间中的身份?
    【解决方案5】:

    来源:http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

    名称修饰是 C++ 编译器使用的过程,它为程序中的每个函数赋予一个唯一的名称。在 C++ 中,通常程序至少有几个同名的函数。因此,名称修饰可以被认为是 C++ 中的一个重要方面。

    示例: 通常,成员名称是通过将成员名称与类名称连接来唯一生成的,例如给定声明:

    class Class1
     {
            public:
                int val;
                ...
      };
    

    val 变成这样:

      // a possible member name mangling
     val__11Class1
    

    【讨论】:

      【解决方案6】:

      agner 有更多关于什么是名称修饰以及它在不同编译器中如何完成的信息。

      名称修饰(也称为名称修饰)是 C++ 使用的一种方法 编译器将附加信息添加到函数名称和 对象文件中的对象。当链接器使用此信息时 一个模块中定义的函数或对象被另一个模块引用 模块。名称修改有以下目的:

      1. 使链接器能够区分不同版本的重载函数。
      2. 使链接器可以检查对象和函数在所有模块中的声明方式是否完全相同。
      3. 使链接器可以在错误消息中提供有关未解析引用类型的完整信息。

      名称修改是为了实现目的 1. 其他目的而发明的 是并非所有编译器都完全支持的次要好处。这 必须为函数提供的最少信息是名称 函数及其所有参数的类型以及任何 类或命名空间限定符。可能的附加信息 包括返回类型、调用约定等。所有这些 信息被编码成单个 ASCII 文本字符串,看起来 对人类观察者来说是神秘的。链接器不必知道什么 此代码意味着为了实现目的 1 和 2。它只需要 检查字符串是否相同。

      【讨论】:

        猜你喜欢
        • 2017-11-28
        • 2011-09-25
        • 2011-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-06
        • 2010-10-15
        相关资源
        最近更新 更多