如何用 C 编写 C 编译器?

这个问题可能源于我对编译器的误解,但是现在开始..。

我们可以在 K & R 第一版的序言中找到以下陈述(第11页) :

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

(我的强调)

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

走出这个无限回归难题(或者鸡和蛋的问题)的唯一方法是,K & R 所指的用 C 编写的 C 编译器实际上是用一个已经存在的用 C 以外的语言编写的 C 编译器编译的。然后用 C 编写的 C 编译器取代了后者。

还是我完全错了?

57595 次浏览

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

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

  • 用 Y 语言为 x 语言实现一个解释器或编译器。尼克劳斯 · 沃思报告说,他在 Fortran 编写了第一个帕斯卡尔编译器。
  • 用于 X 的另一个解释器或编译器已经用另一种语言 Y 编写; Scheme 通常就是这样引导的。
  • 编译器的早期版本是在 X 的一个子集中编写的,其中存在一些其他的编译器; 这就是 Java、 Haskell 和初始 Free Pascal 编译器的一些超集的引导方式。
  • X 的编译器是从另一个存在 X 的编译器的架构交叉编译的; 这就是 C 的编译器通常移植到其他平台的方式。这也是在初始引导之后用于 Free Pascal 的方法。
  • 用 X 编写编译器; 然后从源代码手动编译它(很可能是以未优化的方式) ,并在代码上运行它以获得优化的编译器。Donald Knuth 在他的网络文学编程系统中使用了这个。

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

您可以阅读有关 C 语言历史的 呼叫。您将看到它还与 UNIX 系统强烈链接。

它被称为 自力更生,引用自维基百科:

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

  1. 在语言中实现 X 语言的解释器或编译器 尼克劳斯 · 沃思(Y.Niklaus Wirth)报告说,他在 Fortran.
  2. X 的另一个解释器或编译器已经写入 另一种语言 Y; Scheme 通常就是这样引导的。
  3. 编译器的早期版本是在 X 的子集中编写的 存在其他的编译器,这就是为什么一些超集 和初始的 Free Pascal 编译器是 自力更生。
  4. X 的编译器是从另一个体系结构交叉编译的,其中 存在一个 X 的编译器; 这就是 C 的编译器 通常移植到其他平台。这也是用于 在初始引导后释放 Pascal。
  5. 用 X 编写编译器; 然后从源代码手工编译它(大多数 并在代码上运行它以获得 一个优化的编译器。 Donald Knuth 用这个来编写他的 WEB 文档 编程系统。

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

对于一个编译器来说,用它所编译的语言来编写是再正常不过的事情了。实现这一点的一种方法是用其他语言为 L 语言编写一个完整的编译器,然后用 L 语言为 L 语言编写一个新的编译器。一个更有趣的方法是用其他语言为 L 的一个子集编写一个最小编译器,然后使用这个最小子集来改进编译器,使它不那么最小地增加 L 的可用子集。这样,就可以构建一个完整的编译器。