OP特别要求查看类的情况。下面的图表显示了在左上角的微小C++程序中发生的情况。图表的其余部分显示了方框,代表我们称之为“符号空间”(或“作用域”)的内容,它们本质上是哈希表,将符号名称(每个方框列出它所拥有的符号)映射到DMS对该符号所知道的信息(定义的源文件位置、引用该定义的AST节点列表和表示类型的复杂联合体,可能反过来指向其他类型)。箭头显示符号空间如何连接;从空间A到空间B的箭头表示“作用域A包含在作用域B中”。通常,符号空间查找过程会在作用域A中搜索符号x,如果在A中没有找到x,则会继续在作用域B中搜索。您会注意到箭头上标有一个整数;这告诉搜索机制首先在编号最小的父级作用域中查找,然后再尝试使用具有较大数字的箭头搜索作用域。这就是作用域的排序方式(请注意,Class C继承自A和B;任何在类C中查找字段(例如“b”)的查找都将被强制首先在A的范围内查找,然后在B的范围内查找。通过这种方式,实现了C++的查找规则。
请注意,类名被记录在(唯一的)全局命名空间中,因为它们是在顶层声明的。如果它们在某个明确的命名空间中定义,那么该命名空间将具有自己的相应符号空间,记录已声明的类,并且该命名空间本身将记录在全局符号空间中。 OP并没有询问函数体的符号表长什么样子,但我碰巧有一张说明性幻灯片,如下所示。 符号空间的工作方式相同。 此幻灯片显示了符号空间与其表示的作用域区域之间的链接。 该链接实际上是由与符号空间相关联的指针实现的,指向相应的AST(命名空间定义可以分散在多个位置)。请注意,在这种情况下,函数名称记录在全局命名空间中,因为它在顶层声明。 如果它在类的范围内定义,则函数名称将记录在类体的符号空间中(在前面的图表中)。 作为一项普遍规则,符号表的组织方式完全取决于编译器和设计者的选择。在我们的情况下,我们设计了一个非常通用的符号表管理包,因为我们计划(并且已经)使用相同的包以统一的方式处理多种语言(C、C++、Java、COBOL、几种传统语言)。 然而,符号空间和继承的抽象结构必须在C++编译器中以基本等效的方式实现;毕竟,它们必须建模相同的信息。我期望GCC和Clang编译器中有类似的结构(嗯,整数编号的继承弧可能不一样:)。虽然构建C++解析器本身就非常难, 但构建这样一个符号表则更加困难。这项工作的投入远远超过构建C++解析器。我们的C++名称解析器是一些由属性文法代码编写、以DMS编译和执行的250K SLOC。正确把握细节是一个巨大的头痛;C++参考手册非常庞大、混乱,事实分散在整个文档中,并且在各种地方之间存在矛盾(我们试图将这些问题反映给委员会),或者在编译器之间存在不一致性(我们有GCC和Visual Studio 201x版本)。
更新于2017年3月:现在已经有了C++2014的符号表。 更新于2018年6月:现在已经有了C++2017的符号表。
class A
{
int a;
void f(int, int);
};
这将生成一个包含符号"A","a"和"f"的符号表。通常,为了简化查找,"a"和"f"会被标记为作用域。
"A" -> (class)
"A::a" -> (class variable member)
"A::f(int,int)" -> (class function member)
a
和 f
符号不会存储在顶层符号表中的情况,而是每个命名空间(包括C++命名空间和类)都有自己的符号表,其中包含定义在其中的符号。但这只是一种数据结构选择。您仍然可以将符号表抽象地视为一个平面表,在其中名称映射到构造。简短回答:是的,在Linux上可以使用“nm --demangle”。
长回答:符号表中的函数包含函数名称、返回值以及如果它属于一个类,则还包括类名。但是,名称、类型(不总是)和类别并没有写出其完整名称以使用更少的空间。这些字符串称为解缠。但是您知道这个短名称是唯一的,您可以从中解析出完整的类名。要查看程序的符号表,可以在Linux上使用“nm”命令。
http://linux.about.com/library/cmd/blcmdl1_nm.htm
它使用了--demangle标志来查看原始名称。您可以编译随机的短程序来查看输出结果。
FOO
的表,其中包含一个COLOR=RED | GREEN | BLUE
列,我可以将该表拆分成三个表FOO_RED
、FOO_GREEN
和FOO_BLUE
。我可以将一个带有TYPE = FUNCTION | CLASS | VARIABLE
的符号表拆分成 3 个单独的函数、类和变量表。从逻辑上讲,它们都是相同的,只是方便的问题。 - MSalters