大多数编译器教科书都会告诉你符号表,并经常向你展示关于诸如 Pascal 之类的适度复杂性语言的详细信息。您不会在教科书中找到有关 C++ 符号表的信息。这太神秘了。
我们为我们的 DMS 软件再造工具包提供完整的 C++14 前端。它解析 C++,构建详细的 AST,并执行名称和类型解析,其中包括构建精确的符号表。
以下是我们教程中关于如何使用 DMS 的幻灯片,重点介绍了 C++ 符号表结构。
OP 专门要求了解类发生了什么。下图显示了左上角的微型 C++ 程序的情况。该图的其余部分显示了框,它们表示我们所谓的“符号空间”(或“范围”),它们本质上是哈希表,将符号名称(每个框列出它拥有的符号)映射到 DMS 知道的关于该符号的信息(定义的源文件位置,引用定义的 AST 节点列表,以及表示类型的复杂联合,并且可能反过来指向其他类型)。箭头显示符号空间是如何连接的;从空间 A 到空间 B 的箭头表示“范围 A 包含在范围 B 内”。通常是符号空间查找过程,在范围 A 中搜索符号 x,如果在 A 中找不到 x,将继续在范围 B 中搜索。您会注意到箭头用整数编号;这告诉搜索机制在尝试使用较大数字的箭头搜索范围之前首先查看编号最小的父范围。这就是作用域的排序方式(注意 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 的符号表。