63

请解释什么是名称修饰,它是如何工作的,它解决了什么问题,以及在哪些上下文和语言中使用。名称修改策略(例如,编译器选择什么名称以及为什么选择)是一个加分项。

4

10 回答 10

49

在您选择的编程语言中,如果标识符是从单独编译的单元中导出的,则它需要一个在链接时已知的名称。名称修饰解决了编程语言中标识符重载的问题。(如果在多个上下文中使用相同的名称或具有多个含义,则标识符是“重载的”。)

一些例子:

  • 在 C++ 中,函数或方法get可以在多种类型下被重载。

  • 在 Ada 或 Modula-3 中,函数get可能出现在多个模块中。

多种类型和多个模块涵盖了通常的上下文。

典型策略:

  • 将每种类型映射到一个字符串,并使用组合的高级标识符和“类型字符串”作为链接时名称。在 C++ 中很常见(特别容易,因为重载仅允许用于函数/方法并且仅适用于参数类型)和 Ada(您也可以重载结果类型)。

  • 如果一个标识符在多个模块或命名空间中使用,则将模块名称与标识符名称连接起来,例如,List_get代替List.get.

根据链接时名称中哪些字符是合法的,您可能需要进行额外的修改;例如,可能需要使用下划线作为“转义”字符,以便区分

  • List_my.get->List__my_get

  • List.my_get->List_my__get

(诚​​然,这个例子已经达到了,但作为编译器编写者,我必须保证源代码中不同的标识符映射到不同的链接时名称。这就是名称修改的全部原因和目的。)

于 2009-08-22T02:40:03.417 回答
27

简而言之,名称修饰是编译器更改源代码中标识符名称以帮助链接器消除这些标识符之间的歧义的过程。

维基百科有一篇关于这个主题的精彩文章,其中有几个很好的例子。

于 2009-08-22T00:25:50.967 回答
5

名称修改是编译器修改对象的“已编译”名称的一种方法,以使其与您以一致的方式指定的名称不同。

这允许编程语言灵活地为多个编译对象提供相同的名称,并以一致的方式查找适当的对象。例如,这允许多个具有相同名称的类存在于不同的命名空间中(通常通过将命名空间添加到类名中等)。

许多语言中的运算符和方法重载更进一步——每个方法最终在编译库中都有一个“损坏”的名称,以便允许一种类型的多个方法以相同的名称存在。

于 2009-08-22T00:27:19.063 回答
5

在 python 中,name-mangling 是一个系统,通过该系统,类变量在类内部和外部具有不同的名称。程序员通过在变量名的开头放置两个下划线来“激活”它。

例如,我可以定义一个包含一些成员的简单类:

>>> class Foo(object):
...  def __init__(self):
...   self.x = 3
...   self._y = 4
...   self.__z = 5
... 

在 python 实践中,以下划线开头的变量名是“内部的”,而不是类接口的一部分,因此程序员不应该依赖它。但是,它仍然可见:

>>> f = Foo()
>>> f.x
3
>>> f._y
4

以两个下划线开头的变量名仍然是公共的,但它是名称混乱的,因此更难访问:

>>> f.__z  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__z'

但是,如果我们知道名称修饰是如何工作的,我们就可以理解它:

>>> f._Foo__z
5

即类名在变量名前加上一个额外的下划线。

Python 没有“私人”与“公共”成员的概念;一切都是公开的。名称修饰是程序员可以发送的最强信号,即不应从类外部访问该变量。

于 2009-08-22T01:54:49.507 回答
3

资料来源: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-12T18:14:27.843 回答
0

在 Fortran 中,需要名称修改,因为该语言不区分大小写,这意味着 Foo、FOO、fOo、foo 等都将解析为相同的符号,其名称必须以某种方式规范化。不同的编译器以不同的方式实现 mangling,当与使用不同编译器编译的 C 或二进制对象交互时,这是一个很大的麻烦。例如,GNU g77/g95 总是在小写名称后面添加一个下划线,除非该名称已经包含一个或多个下划线。在这种情况下,添加了两个下划线。

例如下面的例程

    program test
    end program 

    subroutine foo()
    end subroutine

    subroutine b_ar()
    end subroutine
    subroutine b_a_r()
    end subroutine

产生以下错位符号:

0000000000400806 g     F .text  0000000000000006              b_ar__
0000000000400800 g     F .text  0000000000000006              foo_
000000000040080c g     F .text  0000000000000006              b_a_r__

为了从 C 调用 Fortran 代码,必须调用正确修改的例程名称(显然要考虑到可能的不同修改策略以真正独立于编译器)。要从 fortran 调用 C 代码,C 编写的接口必须导出正确损坏的名称并将调用转发到 C 例程。然后可以从 Fortran 调用此接口。

于 2009-08-22T02:08:54.073 回答
0

在设计链接编辑器时,C、FORTAN 和 COBOL 等语言没有名称空间、类、类成员等。需要名称修饰来支持面向对象的功能,例如那些带有不支持它们的链接编辑器的功能。链接编辑器不支持附加功能这一事实经常被忽略;人们通过说由于链接编辑器而需要名称修改来暗示它。

由于支持名称修饰所做的语言要求之间存在很大差异,因此对于如何在链接编辑器中支持它的问题没有简单的解决方案。链接编辑器旨在处理来自各种编译器的输出(对象模块),因此必须有一种通用的方式来支持名称。

于 2017-11-21T00:15:06.730 回答
0

大多数面向对象的语言都提供函数重载特性。 函数重载 如果任何类有多个具有相同名称但参数类型和编号不同的函数,则称它们为重载。函数重载允许您为不同的函数使用相同的名称。

重载函数的方法

  1. 通过更改参数的数量。
  2. 列出项目通过具有不同类型的参数。

名称修饰如何实现函数重载?
C++ 编译器在生成目标代码时会区分不同的函数——它通过根据类型和参数数量添加有关参数的信息来更改名称。这种添加附加信息以形成函数名称的技术称为名称修饰。C++ 标准没有为名称修饰指定任何特定技术,因此不同的编译器可能会将不同的信息附加到函数名称。我已经在 gcc4.8.4 上运行了示例程序。

class ABC
{       
 public:
  void fun(long a, long b) {}
  void fun(float a, float b) {} 
  void fun(int a, float b) {}   
};
int main()
{
 ABC obj;
 obj.fun(1l,2l);
 obj.fun(1,2.3f);
 obj.fun(3.2f,4.2f);
 return 0;
}

该程序有 3 个名为 fun 的函数,根据参数的数量及其类型而有所不同。这些函数名称如下所示:

ayadav@gateway1:~$ nm ./a.out |grep fun
000000000040058c W _ZN3ABC3funEff
00000000004005a0 W _ZN3ABC3funEif
000000000040057a W _ZN3ABC3funEll
  • ABC 是类名的命令字符串
  • fun 是函数名的常用字符串
  • ff 两个 float->f 类型的参数
  • ll 两个 long->l 类型参数
  • 如果第一个整数参数->i 和一个浮点数->f 参数
于 2015-12-24T05:42:04.587 回答
0

这里的答案很棒,所以这只是我的小经验的补充:我使用名称修饰来了解,什么工具(gcc / vs / ...)以及参数如何传递到堆栈以及我是什么调用约定处理,并且基于名称,例如,如果看到 _main我知道这对其Cdecl他人来说是一样的

于 2018-09-26T10:36:18.383 回答
0

以前的所有答案都是正确的,但这里是 Python 的观点/推理示例。

定义

当类中的变量具有 __ 前缀(即两个下划线)且没有 __ 后缀(即两个下划线或更多)时,则将其视为私有标识符。Python 解释器转换任何私有标识符,并将名称修改为 _class__identfier

Example:
MyClassName --> _myClassName
__variable --> __variable

为什么

这是必需的,因为要避免可能由覆盖属性引起的问题。换句话说,为了覆盖,Python 解释器必须能够为子方法和父方法构建不同的 id,并且使用 __(双下划线)使 python 能够做到这一点。在下面的示例中,如果没有 __help,此代码将无法工作。

class Parent:
    def __init__(self):
       self.__help("will take child to school")
    def help(self, activities):
        print("parent",activities)

    __help = help   # private copy of original help() method

class Child(Parent):
    def help(self, activities, days):   # notice this has 3 arguments and overrides the Parent.help()
        self.activities = activities
        self.days = days
        print ("child will do",self.activities, self.days)


# the goal was to extend and override the Parent class to list the child activities too
print ("list parent & child responsibilities")
c = Child()
c.help("laundry","Saturdays")
于 2018-09-09T08:18:59.453 回答