71

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

您可以在 K&R 第一版(第 xi 页)的前言中找到以下声明:

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

(我的重点)

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

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

还是我完全关闭了?

4

4 回答 4

50

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

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

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

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

于 2013-08-15T07:14:39.797 回答
10

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

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

于 2013-08-15T07:14:07.637 回答
9

请参阅维基百科页面的鸡和蛋部分:

如果需要一个语言 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 literate 编程系统中使用了这个。
于 2013-08-15T07:13:57.390 回答
6

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

于 2013-08-15T07:16:06.273 回答