【问题标题】:How can a C compiler be written in C? [duplicate]如何用 C 编写 C 编译器? [复制]
【发布时间】:2013-08-17 08:37:17
【问题描述】:

这个问题可能源于我对编译器的误解,但这里是……

可以在《K&R》第一版(第 xi 页)的前言中找到以下陈述:

操作系统、C 编译器以及基本上所有的 UNIX 应用程序(包括用于编写本书的所有软件)都是用 C 编写的。

(我的重点)

这是我不明白的:C 编译器在编译任何 C 代码之前不是必须先编译自己吗?如果那个 C 编译器是用 C 编写的,编译它不需要现有的 C 编译器吗?!

解决这个无限回归难题(或先有鸡还是先有蛋的问题)的唯一方法是,K&R 所指的用 C 语言编写的 C 编译器实际上是用已经存在的 C 编译器编译的,该编译器是用一种语言编写的除了 C。用 C 编写的 C 编译器随后取代了后者。

还是说我完全不在了?

【问题讨论】:

  • 嗯,你从一个汇编或其他语言开始,一旦你有了它,就可以用它在 C 中制作一个,等等。
  • C 最初是用 B 编写的,它是一个精简的 BCPL,我相信它是一个精简的 Algol 60。 B 可能是用宏汇编器编写的。

标签: c compiler-construction kernighan-and-ritchie


【解决方案1】:

它被称为Bootstrapping,引用自维基百科:

如果需要一个语言 X 的编译器来获得一个语言 X 的编译器(它是用语言 X 编写的),那么第一个编译器是如何编写的?解决这个先有鸡还是先有蛋的问题的可能方法包括:

  1. 在语言中实现语言 X 的解释器或编译器 Y. Niklaus Wirth 报告说他在 Fortran。
  2. 已经为 X 编写了另一个解释器或编译器 另一种语言 Y;这就是 Scheme 经常被引导的方式。
  3. 早期版本的编译器是用 X 的子集编写的 存在其他一些编译器;这就是一些超集的方式 Java、Haskell 和最初的 Free Pascal 编译器是 引导。
  4. X 的编译器是从另一个架构交叉编译的,其中 存在 X 的编译器;这就是 C 的编译器的样子 通常移植到其他平台。这也是用于 在初始引导后释放 Pascal。
  5. 在 X 中编写编译器;然后从源代码手动编译它(大多数 可能以非优化的方式)并在代码上运行以获得 优化的编译器。 Donald Knuth 将其用于他的 WEB 素养 编程系统。

如果您有兴趣,here 是 Dennis Richie 的第一个 C 编译器源代码。

【讨论】:

    【解决方案2】:

    查看Wikipedia page 的鸡与蛋部分:

    如果一个人需要一个语言 X 的编译器来获得一个语言 X 的编译器(它是用语言 X 编写的),那么第一个编译器是如何编写的?解决这个先有鸡还是先有蛋的问题的可能方法包括:

    • 用语言 Y 实现语言 X 的解释器或编译器。Niklaus Wirth 报告说,他用 Fortran 编写了第一个 Pascal 编译器。
    • X 的另一个解释器或编译器已经用另一种语言 Y 编写;这就是 Scheme 经常被引导的方式。
    • 早期版本的编译器是用 X 的一个子集编写的,其中存在一些其他编译器;这就是如何引导 Java、Haskell 和最初的 Free Pascal 编译器的一些超集。
    • X 的编译器是从另一个存在 X 编译器的体系结构交叉编译的;这就是 C 编译器通常移植到其他平台的方式。这也是在初始引导之后用于 Free Pascal 的方法。
    • 在 X 中编写编译器;然后从源代码手动编译它(很可能以非优化方式)并在代码上运行以获得优化的编译器。 Donald Knuth 将其用于他的 WEB 识字编程系统。

    【讨论】:

    • Pascal cuoq... 首先,语言 Y 应该首先有一个编译器,对吗?我认为像 Clojure 这样的语言是这样编写的。你能解释一下“手动编译”吗?
    【解决方案3】:

    通常,第一个编译器是用另一种语言编写的(在这种情况下直接用 PDP11 汇编程序编写,或者大多数“现代”语言用 C 编写)。然后,这个第一个编译器被用来编写一个用该语言本身编写的编译器。

    您可以阅读此page,了解 C 语言的历史。您会看到它也与 UNIX 系统密切相关。

    【讨论】:

    • 小心把 1970 年的真理传到今天。 GCC 切换到 C++ :-)
    • 嗯,C++ 中的 GCC...我对此有点怀疑(也很害怕!:-))。
    【解决方案4】:

    编译器用它所编译的语言编写是完全正常的。实现这一点的一种方法是用其他语言为语言 L 编写一个完整的编译器,然后在 L 中为 L 编写一个新的编译器。更有趣的方法是在某些语言中为 L 的一个子集编写一个最小编译器其他语言,然后使用这个最小子集来改进编译器,使其变得不那么最小,增加L的可用子集。这样就可以构建一个完整的编译器。

    【讨论】:

      猜你喜欢
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      相关资源
      最近更新 更多