什么是名称错误,它是如何工作的?

时间:2009-08-22 00:20:11

标签: language-agnostic name-mangling

请解释名称错位,工作原理,解决的问题以及使用的上下文和语言。命名修改策略(例如编译器选择的名称和原因)加号。

10 个答案:

答案 0 :(得分:36)

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

一些例子:

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

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

多种类型和多个模块涵盖了通常的背景。

典型策略:

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

  • 如果在多个模块或命名空间中使用标识符,请使用标识符的名称加入模块的名称,例如List_get而不是List.get

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

  • List_my.get - > List__my_get

  • List.my_get - > List_my__get

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

答案 1 :(得分:24)

简单地说,名称修改是编译器更改源代码中标识符名称的过程,以帮助linker消除这些标识符之间的歧义。

Wikipedia has a wonderful article on this subject有几个很好的例子。

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:2)

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

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

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

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

>>> 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

即。 classname前置于带有额外下划线的变量名。

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

答案 4 :(得分: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

答案 5 :(得分:0)

在Fortran中,需要进行名称修改,因为该语言不区分大小写,这意味着Foo,FOO,fOo,foo等将全部解析为相同的符号,其名称必须以某种方式进行规范化。不同的编译器以不同的方式实现修改,这在与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调用此接口。

答案 6 :(得分:0)

大多数面向对象语言都提供了函数重载功能。 功能重载 如果任何类具有多个具有相同名称但参数类型不同的函数&amp;数字,然后据说他们超载。函数重载允许您对不同的函数使用相同的名称。

超载功能的方法

  1. 通过更改参数数量。
  2. 列出项目具有不同类型的参数。
  3. 如何通过名称修改实现功能重载?
    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;
    }
    

    这个程序有三个名为fun的函数,根据参数的数量及其类型有所不同。 这些函数名称被破坏如下:

    ayadav@gateway1:~$ nm ./a.out |grep fun
    000000000040058c W _ZN3ABC3funEff
    00000000004005a0 W _ZN3ABC3funEif
    000000000040057a W _ZN3ABC3funEll
    
    • ABC是班级名称的命令字符串
    • fun是函数名称的常用字符串
    • ff两个float-&gt; f类型的参数
    • ll两个long-&gt; l typeof arguments
    • 如果第一个整数参数 - &gt; i和一个float-&gt; f参数

答案 7 :(得分:0)

在设计链接编辑器时,C,FORTAN和COBOL等语言没有名称空间,类,类成员和其他类似的东西。名称修改需要支持面向对象的功能,例如那些带有不支持它们的链接编辑器的功能。链接编辑器不支持其他功能的事实经常被遗漏;人们通过说链接编辑器需要名称修改来暗示它。

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

答案 8 :(得分: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")

答案 9 :(得分:0)

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