C++ 程序的编译阶段是什么?

What are the stages of compilation of a C++ program?

提问人:Luchian Grigore 提问时间:1/12/2012 最后编辑:sbiLuchian Grigore 更新时间:5/2/2018 访问量:17071

问:

C++ 程序的编译阶段是否由标准指定?

如果是这样,它们是什么?

如果没有,那么广泛使用的编译器(我更喜欢 MSVS)的答案会很棒。

我说的是预处理、标记化、解析等。它们的执行顺序是什么,它们具体做了什么?

编辑:我知道编译,链接和预处理的作用,我最感兴趣的是其他和顺序。当然,也欢迎对这些问题的解释,因为我可能不是唯一对答案感兴趣的人。

器构造编译 C++常见问题

评论

3赞 Rup 1/12/2012
这是 GCC 内部手册的页面。我以为它使用了你想要的语言,但显然不是。如果您查看 GCC 源代码,就会发现有大量单独的优化通道。我猜不会,该标准规定了它需要实现什么,而不是它必须如何实现,你最好的选择是学术编译器构建课程或教科书——我相信周围有很多。
0赞 Luchian Grigore 1/12/2012
@sharptooth我回滚了这个问题——我相信寻求同样东西的人更容易找到这个标题。
0赞 sharptooth 1/12/2012
@Luchian Grigore:好的,我只是标题的改变真的很重要——“它通常如何完成”与“标准对应该如何完成的规定”。无论如何,这是你的问题,你决定。
0赞 Cody Gray - on strike 1/12/2012
超出标准中明确提到的任何内容似乎都只是一个实现细节,并不重要。这纯粹是出于好奇,还是您想解决问题?
0赞 Keith Thompson 1/4/2016
否认、愤怒、讨价还价、抑郁、接受。

答:

6赞 Steve Jessop 1/12/2012 #1

9个所谓的“翻译阶段”列在标准中(C++11中的2.2,C++03中的2.1)。[lex.phases]

标准中要求的细节各不相同:预处理分为几个阶段,因为在标准的不同点上,当定义特定行为位时,“已经完成”和“剩下要做”的内容非常重要。因此,虽然它没有告诉你如何编写词法分析器,但它给了你一个非常清晰的路线图。

另一方面,链接主要留给实现来决定它的实际实现方式,因为标准并不关心给定名称是如何查找的,只关心它指的是什么。

它也没有提供任何关于解析的细节,它只是说“生成的标记在语法和语义上进行了分析和翻译”。那是因为整个第3-15章都需要填写这个细节。

它根本没有提到解析/转换过程中的内部表示,也没有提到优化阶段——它们对编译器的设计很重要,但对标准并不重要。优化可以在不同编译器的不同位置进行。在很长一段时间里,优化几乎完全处于编译阶段,在发出目标文件之前,链接器就像一个帖子一样愚蠢。我认为现在严肃的 C++ 实现都可以在多个 TU 上至少进行一些优化。因此,“其他人”不仅被排除在标准之外,它们实际上会随着时间的推移而改变。

3赞 Liam M 1/12/2012 #2

C++ 规范在许多方面故意含糊不清,主要是为了保持实现独立性。语言模糊的许多领域不再是一个大问题 - 例如,您通常可以依赖 8 位的字符。然而,其他问题,如使用多重继承的结构的布局,以及虚函数对类的影响,也是一个真正的问题。这些问题会影响使用不同编译器生成的代码的兼容性。C++ 的应用程序二进制接口(或 ABI)没有严格定义,因此您偶尔不得不涉足 C,这会成为问题。编写插件接口就是一个很好的例子。

同样,该标准也没有详细说明编译器应该如何构建,因为有许多关键的决策和功能可以区分编译器。例如,MSVC 可以执行部分生成(允许编辑并继续),而 GCC 则不执行。不过,一般来说,所有编译器都执行类似的阶段:预处理、语法解析、确定程序流、生成符号表以及生成一系列线性指令,这些指令随后可以链接以生成可执行文件。哦,链接这些对象文件,这通常由链接器完成。

我简要地看了一下,很难找到各个编译器的描述。我怀疑像Microsoft这样的商业编译器有很多东西,纯粹是出于商业原因。GCC 是您最好的选择,尽管 Microsoft 很乐意描述这个过程。不过,这是非常平庸的东西:编译器的工作方式几乎相同。真正的黄金在于他们如何执行这些阶段,他们使用的算法和数据结构。在这方面,我推荐这本书。几年前,我为一门大学课程买了一本全新的书,:)我从图书馆借来了大部分教科书。

46赞 Keith Thompson 1/12/2012 #3

C++ 程序的编译阶段是否由标准指定?

是的,也不是。

C++ 标准定义了 9 个“翻译阶段”。引用日期为 2011-02-28(在官方 C++11 标准发布之前)的 N3242 草案 (10MB PDF),第 2.2 节:

翻译语法规则的优先级由以下阶段指定 [见脚注]。

  1. 物理源文件字符以实现定义的方式映射到基本源字符集 (为行尾指示符引入换行符) 如果 必要。[剪]
  2. 删除紧跟换行符的反斜杠字符 (\) 的每个实例,将物理源行拼接到 形成逻辑源行。[剪]
  3. 源文件被分解为预处理标记 (2.5) 和空格字符序列(包括注释)。[剪]
  4. 执行预处理指令,扩展宏调用,并执行_Pragma一元运算符表达式。[剪]
  5. 字符文本或字符串文本中的每个源字符集成员,以及每个转义序列和通用字符名称 在字符文本或非原始字符串文本中,转换为 执行字符集的相应成员;[剪]
  6. 相邻的字符串文字标记是串联的。
  7. 分隔标记的空格字符不再重要。每个预处理令牌都转换为一个令牌。(2.7). 的 对生成的标记进行语法和语义分析,并 翻译为翻译单元。[剪]
  8. 翻译的翻译单元和实例化单元的组合如下:[SNIP]
  9. 所有外部实体引用都将被解析。链接库组件以满足对 当前翻译。所有此类转换器输出都收集到 程序映像,其中包含在其 执行环境。

[脚注]实现必须表现得像这些单独的阶段一样,尽管在实践中不同的阶段可能会折叠在一起。

正如 [SNIP] 标记所示,我没有引用整个部分,足以表达这个想法。

需要强调的是,编译器不需要遵循这个确切的模型,只要最终结果和他们一样。

阶段 1-6 或多或少对应于预处理器,7 对应于您通常认为的编译,8 对应于模板,9 对应于链接。

(C 的翻译阶段类似,但省略了 #8。

评论

0赞 Steve Jessop 1/12/2012
在 gcc 阶段 6 中,FWIW 不是预处理的一部分(不连接相邻的字符串文字)。我手头没有其他编译器可以比较。gcc -E
1赞 rodrigo 1/12/2012
A footnote in this page says: "Implementations must behave as if these separate phases occur, although in practice different phases might be folded together".
0赞 Keith Thompson 1/13/2012
@rodrigo: I already quoted that footnote. You probably missed it because I didn't put it at the bottom of the quote. I've just moved it there.