【问题标题】:What are the stages of compilation of a C++ program?C++程序的编译阶段是什么?
【发布时间】:2012-02-08 15:36:20
【问题描述】:

标准是否规定了 C++ 程序的编译阶段?

如果有,它们是什么?

如果不是,一个广泛使用的编译器的答案(我更喜欢 MSVS)会很棒。

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

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

【问题讨论】:

  • 这里是the GCC internals manual's page。我认为它使用了您想要的语言,但显然不是。如果您查看 GCC 源代码,则会有大量单独的优化过程。我猜不,该标准指定了它需要实现的目标而不是它必须如何实现,并且您最好的选择是学术编译器构建课程或教科书 - 我相信周围有很多。
  • @sharptooth 我回滚了这个问题 - 我相信这个标题更容易被寻求相同事物的人找到。
  • @Luchian Grigore:好吧,我只是标题更改真的很重要——“它通常是如何完成的”v“标准对它应该如何完成的规定”。无论如何,这是你的问题,你决定。
  • 标准中明确提及的内容之外的任何内容似乎都是无关紧要的实现细节。这纯粹是出于好奇,还是您想解决问题?
  • 否认、愤怒、讨价还价、抑郁、接受。

标签: c++ compiler-construction compilation c++-faq


【解决方案1】:

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

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

我看了一眼,很难找到个别编译器的描述。我怀疑像微软提供的商业编译器有很多,纯粹是出于商业原因。 GCC 是你最好的选择,although Microsoft is happy to describe the process. 虽然这是相当平庸的东西:编译器的工作方式几乎相同。真正的黄金在于他们如何执行这些阶段,他们使用的算法和数据结构。在这方面,I recommend this book。几年前我为一门大学课程买了一本全新的副本,我的大部分教科书都是从图书馆借来的:)。

【讨论】:

    【解决方案2】:

    标准是否规定了 C++ 程序的编译阶段?

    是和不是。

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

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

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

    [footnote] 实现必须表现得好像这些单独的阶段发生了一样,尽管在实践中不同的阶段可能被折叠在一起。

    [SNIP] 标记所示,我没有引用整个部分,只是足以理解这个想法。

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

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

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

    【讨论】:

    • FWIW,在 gcc 阶段 6 中不是预处理的一部分(gcc -E 不会连接相邻的字符串文字)。我没有其他编译器可以比较。
    • 本页的脚注说:“实施必须表现得好像这些单独的阶段发生了一样,尽管在实践中不同的阶段可能被折叠在一起”。
    • @rodrigo:我已经引用了那个脚注。你可能错过了它,因为我没有把它放在报价的底部。我刚把它移到那里。
    【解决方案3】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-05-30
      • 1970-01-01
      • 2023-03-13
      • 2015-04-30
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 2010-10-24
      • 1970-01-01
      相关资源
      最近更新 更多