C++ 代码的 Symbol table 是否包含函数名称以及 class 名称?

Does Symbol table for C++ code contain function names along with class names?

我一直在搜索各种 post 关于 C++ 代码的符号 table 是否包含函数名称以及 class 名称。我可以在 post 上找到的东西是它取决于编译器的类型,

如果它一次性编译代码,那么它不需要在你的符号中存储 class 名称和子程序名称 table

但如果它是多通道编译器,它可以添加有关它遇到的 class(es) 及其子例程的信息,以便它可以进行参数类型检查并发出有意义的错误消息。

我不明白它是否真的依赖于编译器?我假设编译器(对于 C++ 代码)会将带有 class 名称的函数名称放在 table 中,无论它是单通道还是多通道编译器。它如何依赖于通行证?我没有这样的 great/deep 知识。 此外,任何人都可以显示一个简单的 C++ class 的示例符号 table,它会是什么样子(带有 class 名称的函数名称)?

符号 table 将名称映射到程序中的构造。因此,它用于记录 类、函数、变量以及程序中具有用户指定名称的任何其他名称。

(有两种常见的符号 table - 一种是编译器在编译程序时维护的,另一种存在于目标文件中,以便可以链接到其他对象。这两个是强相关的,但不需要在内部有相似的表示。通常只有编译器符号 table 中的一些符号会输出到对象中)。

你说的部分没有意义:

if it compiles code in one-pass then it will not need to store class name and subroutine names in your symbol table

如果无法在符号 table 中查找名称,编译器如何确定它所引用的结构?

but if it is a multi-pass compiler, it could add information about the class(es) it encounters and their subroutines so that it could do argument type checking and issue meaningful error messages.

没有理由不能一次完成。

I could not understand whether it is actually compiler dependent or not?

所有编译器都将使用一个符号 table,但它的使用将隐藏在实现中。

I was assuming that compiler(for C++ code) would put function names with class names in the table whether it is single pass or multi pass compiler. How is it dependent on the passes?

什么 是如何依赖于通行证的?所有名称都放在符号 table 中——这就是它的用途——通常符号解析对于编译器所做的几乎所有其他事情都很重要,因此需要尽早完成(即在第一遍中——事实上多遍编译器编译器中第一遍的主要目的可能只是构建符号 table!).

Moreover, could anyone show a sample symbol table for a simple C++ class, how would it look like (function names with class name)?

我来试试看:

class A
{
    int a;
    void f(int, int);
};

将生成包含符号 "A"、"a" 和 "f" 的符号 table。通常 "a" 和 "f" 会标记一个范围以简化查找,例如:

"A"  -> (class)
"A::a"  ->  (class variable member)
"A::f(int,int)"  ->  (class function member)

也有可能 af 符号不会存储在顶级符号 table 中,而是每个名称 space(包括C++ namespaces and 类) 将有自己的符号 table,包含其中定义的符号。但可以说,这只是一种数据结构选择。您仍然可以抽象地将符号 table 视为平面 table,其中名称映射到构造。

通常 "A::a" 符号不会输出到目标文件,因为链接不需要它。

简短回答:是的,在 linux

上使用 'nm --demangle'

长答案:符号 table 中的函数包含函数名称加上 return 值,如果它属于 class,则 class 名称也。但是名称、类型(不总是)和 classes 不是用它的全名来写的,以少用 space。这个字符串称为 demangle。但是您知道这个短名称是唯一的,您可以从中解析完整的 class 名称。要查看程序的符号 table,您可以在 linux 上使用 'nm'。

http://linux.about.com/library/cmd/blcmdl1_nm.htm

它获得了 --demangle 标志以查看原始名称。大家可以随意编一个小程序,看看结果如何。

大多数编译器教科书都会告诉您有关符号 table 的信息,并且通常会向您展示有关适度复杂性语言(例如 Pascal)的详细信息。您不会在教科书中找到有关 C++ 符号 table 的信息;太神秘了。

我们为 DMS 软件再造工具包提供完整的 C++14 前端。它解析 C++,构建 detailed ASTs,并执行名称和类型解析,其中包括构建一个精确的符号 table。

接下来是我们关于如何使用 DMS 的教程中的幻灯片,重点是 C++ 符号 table 结构。

OP 特别要求了解 classes 会发生什么。下图显示了左上角的微型 C++ 程序。图表的其余部分显示了方框,它们代表我们所说的 "symbol spaces"(或 "scopes"),它们本质上是散列 tables 映射符号名称(每个方框列出它拥有的符号)到DMS 知道的有关该符号的信息(定义的源文件位置、引用该定义的 AST 节点列表以及表示该类型的复杂联合,并且可能反过来指向其他类型)。箭头表示符号 space 是如何连接的;从 space A 到 space B 的箭头表示 "scope A is contained within scope B"。通常,符号 space 查找过程会在范围 A 中搜索符号 x,如果在 A 中找不到 x,则将在范围 B 中继续搜索。您会注意到箭头用整数编号;这告诉搜索机制首先查看编号最少的父范围,然后再尝试使用具有较大数字的箭头搜索范围。这就是范围的排序方式(注意 Class C 继承自 A 和 B;在 class C 中的任何字段查找(例如 "b" 将被迫首先在 A 的范围中查找,然后在B的作用域中。这样就实现了C++的查找规则。

请注意,class 名称记录在(唯一)全局名称space 中,因为它们是在顶级声明的。如果它们已经在某个明确的名称space中定义,那么名称space将有一个对应的符号space它自己记录声明的classes,以及名称space 本身将记录在全局符号 space 中。

OP 没有询问函数体的符号 table 是什么样子,但我恰好在下面也有一张说明性幻灯片。 符号 spaces 的工作方式相同。这张幻灯片中显示的是符号 space 和它所代表的范围区域之间的联系。该链接实际上是通过与符号 space 关联的指针实现的,指向相应的 AST(s,namespace 定义可以分散在多个地方)。

请注意,在这种情况下,函数名称记录在全局名称中space,因为它是在顶层声明的。如果它是在 class 的范围内定义的,函数名称将记录在 class 主体的符号 space 中(在上图中)。

作为一般规则,细节符号table的组织方式完全取决于编译器和设计者的选择。在我们的案例中,我们设计了一个非常通用的符号 table 管理包,因为我们计划(并且已经)使用相同的包来处理多种语言(C、C++、Java、COBOL、几种遗留语言)一种统一的方式。 但是,符号 space 和继承的抽象结构必须在 C++ 编译器中以本质上等效的方式实现;毕竟,他们必须对相同的信息进行建模。我期望 GCC 和 Clang 编译器中有类似的结构(好吧,整数编号的继承弧,也许不是:)

实际上,您的编译器有多少 "passes" 并不重要。它几乎必须构建这些结构来记住它对符号的了解, 一个通道内和通道之间。

虽然 building a C++ parser is very hard by itself,但构建这样的符号 table 要困难得多。这种努力使构建 C++ 解析器的努力相形见绌。我们的 C++ 名称解析器是由 DMS 编译和执行的大约 250K SLOC 属性语法代码。获取详细信息权利是一件非常令人头疼的事情; C++ 参考手册非常庞大,令人困惑,事实散布在整个文档的各处,并且在很多地方它是矛盾的(我们试图向委员会发送有关此的投诉)和/或编译器之间不一致(我们有 GCC 版本和 Visual Studio 201x).

2017 年 3 月更新:现在 C++2014 有符号 tables。 2018 年 6 月更新:现在 C++2017 有符号 tables。