我最近进行了一次 C++ 面试,有人问我,编译器如何区分两个不同类中具有相同名称的静态数据成员?
由于所有静态数据变量都存储在数据段中,因此编译器必须有一种方法可以跟踪哪些静态数据属于哪个类,尤其是当它们具有相同名称时。
编辑:我回答了姓名修饰,但他拒绝说姓名修饰仅在同一班级的成员中使用。
我最近进行了一次 C++ 面试,有人问我,编译器如何区分两个不同类中具有相同名称的静态数据成员?
由于所有静态数据变量都存储在数据段中,因此编译器必须有一种方法可以跟踪哪些静态数据属于哪个类,尤其是当它们具有相同名称时。
编辑:我回答了姓名修饰,但他拒绝说姓名修饰仅在同一班级的成员中使用。
这些名称与它们的类名混合在一起。使用 clang 编译器的示例
class A {
static int i;
};
int A::i = 0;
输出
$ clang++ -cc1 -emit-llvm main1.cpp -o -
; ModuleID = 'main1.cpp'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32"
target triple = "i386-pc-linux-gnu"
@_ZN1A1iE = global i32 0, align 4
_ZN1A1iE
在哪里
$ c++filt _ZN1A1iE
A::i
它是由实现定义的,因此没有一种方法可以完成。
不过,名称修饰是常用的。
好吧,我假设通过名称修饰类的名称也与数据一起编码。如果它像前缀名称一样简单,我不会感到惊讶:
class one
{
static int data;
};
class two
{
static int data;
};
可以有名字
one::data
two::data
在数据段中。这些是独一无二的名字。
名称只是人类用来赋予事物意义的事物。
一旦编译器知道它们是不同的项目(因为它们在不同的类中声明),它就不会使用名称在二进制文件中区分它们,它将使用指针和指针不关心名称:)
然后没有什么禁止在封闭类前面加上数据的名称..
所有“名称修改”的答案都解决了调试器如何找到每个符号的存储以及链接器如何知道差异,但编译器知道,因为这些实体在符号表中占据了不同的条目。
而已。
可执行文件根本不需要知道任何有关名称的信息{*}。只是地点。编译器知道名称,并且该知识被编码在符号表中(但是可以实现)。
{*} 好吧,无论如何,在没有 RTTI 的情况下。
类的名称限定了成员的名称:myclass::mymemb
. 当从类外部引用它们时,编译器会以与您相同的方式区分它们。
在实现中,这是通过name mangling完成的;编译器对类的名称和成员的名称进行了整理,并将它们连接在一起,形成一团糟,例如__i_7myclass_6mymemb
.
名称修改过程(或其必要性)是我们需要extern "C"
与定义非修改名称的 C 接口兼容的原因。
static
一个类的成员的名字将被修改,这样类名是链接器看到的“全局”名称的一部分。
它将类名称附加到数据成员名称。(然后命名破坏了整个事情。)