【问题标题】:Where exactly is the boundary between a preprocessor and a compiler?预处理器和编译器之间的界限到底在哪里?
【发布时间】:2011-11-03 10:29:45
【问题描述】:

根据各种来源(例如,the SE radio episode with Kevlin Henney,如果我没记错的话),“带类的 C”是用预处理器技术实现的(然后将输出馈送到 C 编译器),而 C++ 一直是实现的使用编译器(早期恰好吐出 C)。这似乎引起了一些混乱,所以我想知道:

预处理器和编译器之间的界限到底在哪里?什么时候把实现一种语言的软件叫做“预处理器”,什么时候把它叫做“编译器”?

顺便说一句,“编译语言”是一个既定术语吗?如果有,具体是什么意思?

【问题讨论】:

  • 关于“编译语言”:它被使用,但很少伴随定义,并且经常被那些不知道有非 AOT 编译和编译到机器代码以外的目标的人,或者有一个过于严格的编译定义,不包括上述内容 - 如果没有将其定义得太严重,我不会使用该术语的文本;)

标签: c++ compiler-construction preprocessor terminology


【解决方案1】:

这是一个有趣的问题。我不知道一个明确的答案,但如果按下,我会这样说:

预处理器不解析代码,而是扫描嵌入的模式并展开它们

编译器实际上通过构建 AST(抽象语法树)来解析代码,然后将其转换为不同的语言

【讨论】:

    【解决方案2】:

    预处理器的输出语言是输入语言的子集。

    编译器输出的语言(通常)与输入的语言(机器代码)非常不同。

    【讨论】:

    • @Fred & Janusz:因此“通常”
    【解决方案3】:

    从简化的个人观点:

    我认为预处理器是任何形式的文本操作,它没有底层语言的概念(即:语义或构造),因此仅依赖于自己的一组规则来履行其职责。

    编译器在规则和规则应用于正在处理的内容时启动(是的,它使“我的”预处理器成为编译器,但为什么不是:P),这包括语法和词法检查,以及包含的来自 x (文本)到 y(二进制/中间形式)。正如我的一位教授所说:“它是一个具有输入、过程和输出的系统”。

    【讨论】:

      【解决方案4】:

      C/C++ 编译器关心类型正确性,而预处理器只是扩展符号。

      【讨论】:

      • 动态语言编译器呢?
      • @delnan:你是说脚本解释器吗?他们不是编译器,是吗?
      • 没有。编译器。编译器只是一个从一种编程语言转换为另一种编程语言的程序(或者,如果您觉得特别严格,可以使用较低级别的语言)。没有人说过类型检查等 - 这是某些语言需要的检查,但它不能决定程序是否是编译器。最广泛使用的动态类型语言被编译(到字节码,但同样,确切的输出格式并不重要)。甚至有几十个编译器(主要用于 Lisp 方言)编译成机器码(Lisp 也是动态类型的)。​​
      【解决方案5】:

      编译器由多个进程(组件)组成。预处理器只是其中之一,也是相对最简单的一种。

      来自维基百科文章,Division of compiler processes

      除了最小的编译器之外,所有编译器都有两个以上的阶段。然而, 这些阶段通常被视为前端的一部分或 后端。这两端的交汇点是开放的 辩论。

      前端一般被认为是语法的地方 和语义处理发生,随着翻译到较低 表示级别(比源代码)。

      中间端通常是 旨在对源代码以外的表单执行优化 或机器码。这种源代码/机器代码独立性是 旨在使通用优化能够在版本之间共享 支持不同语言和目标处理器的编译器。

      后端从中间获取输出。它可能会执行更多 针对特定的分析、转换和优化 计算机。然后,它为特定的处理器和操作系统生成代码。”

      预处理只是前端工作的一小部分。

      第一个在现有 C 编译器工具集前面附加额外进程的 C++ 编译器,不是因为它的设计好,而是因为时间和资源有限。

      现在,我认为这种非本地 C++ 编译器无法在商业领域生存。

      我敢说cfront for C++11 是不可能的。

      【讨论】:

        【解决方案6】:

        答案很简单。 预处理器将文本作为输入,并将文本作为输出。例如旧的 unix 命令 m4、cpp(C 预处理器),以及 roff 和 nroff 和 troff 等 unix 程序,它们在哪里(现在仍然是)用于格式化手册页(unix 命令“man”)或格式化文本用于印刷或排版。 预处理器非常简单,它们对所处理的“文本语言”一无所知。换句话说,它们通常处理自然语言。除了名称之外的 C 预处理器,例如仅识别#define、#include、#ifdef、#ifndef、#else 等,如果您使用#define MACRO,它会尝试在找到它的任何地方“扩展”该宏。但这不需要是 C 或 C++ 程序文本,它也可以是用意大利文或希腊文写的小说。 交叉编译成不同语言的编译器通常称为翻译器。因此,发出 C 代码的 C++ 的旧 cfront“编译器”是 C++ 翻译器。 预处理器和后来的翻译器在历史上被使用,因为旧机器只是缺乏内存,无法在一个程序中完成所有事情,而是由专门的程序完成,从磁盘到磁盘。 一个典型的 C 程序将从各种来源编译。并且构建过程将通过 make 进行管理。在我们的时代,C 预处理器通常直接构建到 C/C++ 编译器中。典型的 make 运行会在 *.c 文件上调用 CPP 并将输出写入不同的目录,从那里 C 编译器 CC 会将其直接编译为机器代码,或者更常见的是将汇编代码输出为文本。注意:c 编译器只检查语法,它并不真正关心类型安全等。然后汇编器将采用该汇编器代码并输出一个 *.o 文件,稍后可以与其他 *.o 文件和 *.lib 链接文件转换成可执行程序。 OTOH,您可能有一个 make 规则,它不会调用 C 编译器,而是调用 lint 命令,即 C 语言分析器,它正在寻找典型的错误和错误(被 c 编译器忽略)。 在维基百科(或使用 man 的机器终端)上查找有关 lint、nroff、troff、m4 等的信息非常有趣;D

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-06-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-28
          • 2011-03-30
          • 1970-01-01
          • 2011-07-18
          相关资源
          最近更新 更多