27

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

(1) 来自devx

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

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

(2) 来自维基

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

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

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

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

谢谢并恭祝安康!

4

6 回答 6

29

C 不进行名称修饰,尽管它确实在函数名称前加上下划线,所以printf(3)它实际上是_printf在 libc 对象中。

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

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

namespace zoom
{
    void boom( const std::string& s )
    {
        throw std::runtime_error( s );
    }
}

~$ nm a.out | grep boom
0000000100001873 T __ZN4zoom4boomERKSs

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

编辑:

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

于 2010-05-30T03:18:15.040 回答
21

修饰只是编译器使链接器满意的方式。

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

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

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

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

于 2010-05-30T05:29:51.287 回答
9

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

于 2010-05-30T02:26:50.597 回答
5

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

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

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

于 2010-05-30T02:17:08.007 回答
2

资料来源: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
于 2014-03-21T07:58:04.207 回答
0

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

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

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

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

于 2019-04-25T06:33:38.717 回答