本节书摘来自异步社区《操作系统真象还原》一书中的第0章,第0.17节,作者:郑钢著,更多章节内容可以访问云栖社区“异步社区”公众号查看

0.17 先有的语言,还是先有的编译器,第1个编译器是怎么产生的

首先肯定的是先有的编程语言,哪怕这个语言简单到只有一个符号。先是设计好语言的规则,然后编写能够识别这套规则的编译器,否则若没有语言规则作为指导方向,编译器编写将无从下笔。

第1个编译器是怎么产生的?这个问题我并没有求证,不过可以谈下自己的理解,请大伙儿辩证地看。

这个问题属于哲学中鸡生蛋、蛋生鸡的问题,这种思维回旋性质的本源问题经常让人产生迷惑。可是现实生活中这样的例子太多了。

(1)英语老师教学生英语,学生成了英语老师后又可以教其他学生英语。

(2)写新的书需要参考其他旧书,新的书将来又会被更新的书参考,就像本书编写过程一样,要参考许多前辈的著作。

(3)用工具可以制造工具,被制造出来的工具将来又可以制造新的工具。

(4)编译器可以编译出新的编译器。

这种自己创造自己的现象,称为自举。

自举?是不是自己把自己举起来?是的,人是不能把自己举起来的,这个词很形象地描述了这类“后果必须有前因”的现象。

以上前三个列举的都是生活例子,似乎比第4个更容易接受。即使这样,对于前三个例子大家依然会有疑问。

(1)第一个会英语的人是谁教的?

(2)第一本书是怎样产生的?

(3)第一个工具是如何制造出来的?

其实看到第2个例子大家就可能明白了,世界上的第一本书,它的知识来源肯定是人的记忆,通过向个人或群众打听,把大家都认同的知识记录到某个介质上,这样第一本书就出生了。此后再记录新的知识时,由于有了这本书的参考,不需要重新再向众人打听原有知识了,从此以后便形成了书生书的因果循环。

从书的例子可以证明,本源问题中的第一个,都是由其他事物创建出来的,不是自己创造的自己。

就像先有鸡还是先有蛋一样,一定是先有其他生命体,这个生命体不是今天所说的鸡。伴随这个生命体漫长的进化中,突然有一天它具备了生蛋的能力(也许这个蛋在最初并不能孵化成鸡,这个生命体又经过漫长的进化,最终可以生出能够孵化成鸡的蛋),于是这个蛋可以生出鸡了。过了很久之后,才有的人类。人一开始接触的便是现在的鸡而不知道那个生命体的存在,所以人只知道鸡是由蛋生出来的。

很容易让人混淆的是编译C语言时,它先是被编译成汇编代码,再由汇编代码编译为机器码,这样很容易让人误以为一种语言是基于一种更底层的语言。

似乎没有汇编语言,C语言就没有办法编译一样。拿gcc来说,其内部确实要调用汇编器来完成汇编语言到机器码的翻译工作。因为已经有了汇编语言编译器,那何必浪费这个资源不用,自己非要把C语言直接翻译成机器码呢,毕竟汇编器已经无比健壮了,将C直接变成机器码这个难度比将C语言翻译为汇编语言大多了,这属于重新造轮子的行为。

曾经我就这样问过自己,PHP解释器是C语言写的,C编译器是汇编写的(这句话不正确),汇编是谁写的呢?后来才知道,编译器GCC其实是用C语言写的。乍一听,什么?用C语言写C编译器?自己创造自己,就像电影超验骇客一样。当时的思维似乎陷入了死循环一样,现在看来这不奇怪。其实编译器用什么语言写是无所谓的,关键是能编译出指令就行了。编译出的可执行文件是要写到磁盘上的,理论上,只要某个进程,无论其是不是编译器,只要其关于读写文件的功能足够强大,可以往磁盘上写任意内容,都可以生成可执行文件,直接让操作系统加载运行。想象一下,用Python写一个脚本,功能是复制一个二进制可执行文件,新复制出来的文件肯定是可以执行的。那Python脚本直接输出这样的一个二进制可执行文件,它自然就是可以直接执行的,完全脱离Python解释器了。

编译器其实就是语言,因为编译器在设计之初就是先要规划好某种语言,根据这个语言规则来写合适的编译器。所以说,要发明一种语言,关键是得写出与之配套的编译器,这两者是同时出来的。最初的编译器肯定是简单粗糙的,因为当时的编程语言肯定不完善,顶多是几个符号而已,所以难以称之为语言。只有功能完善且符合规范,有自己一套体系后才能称之为语言。不用说,这个最初的编译器肯定无法编译今天的C语言代码。编程语言只是文本,文本只是用来看的,没有执行能力。最初的编译器肯定是用机器码写出来的。这个编译器能识别文本,可以处理一些符号关键字。随着符号越来越多,不断地改进这个编译器就是了。

以上的符号就是编程语言。后来这个编译器支持的关键字越来越多了,也就是这个编译器支持的编程语言越发强大了,可以写出一些复杂的功能的时候,干脆直接用这个语言写个新的编译器,这个新的编译器出生时,还是需要用老的编译器编译出来的。只要有了新的编译器,之后就可以和老的编译器说拜拜了。发明新的编译器实际上就是为了能够处理更多的符号关键字,也就是又有新的开发语言了,这个语言可以是全新的,也可以是最初的语言,这取决于编译器的实现。这个过程不断持续,不断进化,逐渐才有了今天的各种语言解释器,这是个迭代的过程。

图0-8所示这张图片在网络上非常火,它常常与励志类的文字相关。起初看到这个雕像在雕刻自己时,我着实被感动了,感受到的是一种成长之痛。今天把它贴过来的目的是想告诉大家,起初的编译器也是功能简单,不成规范,然而经过不断自我“雕刻”,它才有了今天功能的完善。

下面的内容我参考了别人的文章,由于找不到这位大师的署名,只好在此先献上我真挚的敬意,感谢他对求知者的奉献。

要说到C编译器的发展,必须要提到这两位大神——C语言之父Dennis Ritchie和Ken Thompson。Dennis和Ken在编程语言和操作系统的深远贡献让他们获得了计算机科学的最高荣誉——Dennis和Ken于1983年赢得了ACM图灵奖 。

编译器是靠不断学习、积累才发展起来的,这是自我学习的过程,下面来看看他们是如何让编译器长大的。

起初的C编译器中并没有处理转义字符,为叙述方便,我们现在称之为老编译器。如果待编译的代码文件中有字符串'',在老编译器眼里,这就是''字符串,并不是转义后的单个字符''。为了表明编译器与作为其输入的代码文件的关系,我们称作为输入的代码文件为应用程序文件,毕竟虽然待编译的代码文件实现了一个编译器,但在编译器眼里,它只是一个应用程序级角色。例如,gcc –c a.c中,a.c就是应用程序文件。

现在想在编译器中添加对转义字符的支持,那就需要修改老编译器的源代码,假设老编译器的源代码文件名为compile_old.c。被修改后的编译器代码,已不属于老编译器的源代码,故我们命名其文件名为compile_new_a.c,下面是修改后的内容。

代码compile_new_a.c

用老编译器将新编译器的源代码compile_new_a.c编译,生成可执行文件,该文件就是新的编译器,我们取名为新编译器_a。为了方便理清它们的关系,将它们列入表格中。

这下编译出来的新编译器_a可以编译含有转义字符''的应用程序代码了,也就是说,待编译的文件(也就是应用程序代码)中,应该用''来表示''。而单独的字符''在新编译器_a中未做处理而无法通过编译。所以此时新编译器_a是无法编译自己的源代码compile_new_a.c的,因为该源文件中只是单个''字符,新编译器_a只认得''。

先更新它们的关系,见下表。

也就是说,现在新编译器_a无法编译自己的源文件compile_new_a.c,只有老编译器才能编译它。

分析一下,新编译器_a无法正确编译自己的源文件compile_new_a.c,其原因是compile_new_a.c中''字符应该用转义字符的方式来引用,即所有用''的地方都应该替换为''。再啰嗦一下,请见新编译器_a的源代码compile_new_a.c,它只处理了字符串'',单个''没有对应的处理逻辑。下面修改代码,将新修改后的代码命名为compile_new_b.c。

代码compile_new_b.c

其实compile_new_b.c只是更新了转义字符的语法,这是新编译器_a所支持的新的语法,此文件是否是编译器源码没什么关系。所以下面还是以新编译器_a来编译新的编译器。

用新编译器_a编译此文件,将生成新编译器_b,将新的关系录入到表格中。

现在想加上换行符'n'的支持。

由于现在编译器还不认识'n',故这样做肯定不行,不过可以用其ASCII码来代替,将其命名为compile_new_c.c。

代码compile_new_c.c

用新编译器_a来编译compile_new_c.c,将生成新编译器_c。

最后再修改compile_new_c.c为compile_new_d.c,将10用'n'替代。

代码compile_new_d.c

用新编译器_c编译compile_new_d.c,生成新编译器d,将直接识别'n'。

编译器经过这样不断的训练,功能越来越强大,不过体积也越来越大了。

《操作系统真象还原》——0.17 先有的语言,还是先有的编译器,第1个编译器是怎么产生的...相关推荐

  1. 《操作系统真象还原》——0.7 内存访问为什么要分段

    本节书摘来自异步社区<操作系统真象还原>一书中的第0章,第0.7节,作者:郑钢著,更多章节内容可以访问云栖社区"异步社区"公众号查看 0.7 内存访问为什么要分段 按理 ...

  2. 《操作系统真象还原》——0.25 指令集、体系结构、微架构、编程语言

    本节书摘来自异步社区<操作系统真象还原>一书中的第0章,第0.25节,作者:郑钢著,更多章节内容可以访问云栖社区"异步社区"公众号查看 0.25 指令集.体系结构.微架 ...

  3. 《操作系统真象还原》第六章 ---- 开启c语言编写函数时代 首挑打印函数小试牛刀 费心讨力重回gcc降级 终尝多日调试之喜悦

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 修改代码的小闲聊 编写print.S(实现打印函数) print.S代码 print.h代码和stdint.h代码 修改main ...

  4. 《操作系统真象还原》——0.23 操作系统是如何识别文件系统的

    本节书摘来自异步社区<操作系统真象还原>一书中的第0章,第0.23节,作者:郑钢著,更多章节内容可以访问云栖社区"异步社区"公众号查看 0.23 操作系统是如何识别文件 ...

  5. 《操作系统真象还原》——0.28 MBR、EBR、DBR和OBR各是什么

    本节书摘来自异步社区<操作系统真象还原>一书中的第0章,第0.28节,作者:郑钢著,更多章节内容可以访问云栖社区"异步社区"公众号查看 0.28 MBR.EBR.DBR ...

  6. 《操作系统真象还原》第九章 ---- 终进入线程动斧开刀 豁然开朗拨云见日 还需解决同步机制才能长舒气

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 进程 线程的自我小理解 线程 进程的状态 内核级线程 & 用户级线程 初步实现内核级线程 浪费两三个小时调试的辛酸史 编写thread ...

  7. 操作系统真象还原实验记录之实验十一:实现中断处理(二)

    操作系统真象还原实验记录之实验十一:实现中断处理(二) 书p335 7.6.2 改进中断处理程序,并调快时钟 1.实验代码第一次修改 对应 书p335 7.6.2 改进中断处理程序 这次是上一次实验的 ...

  8. 《操作系统真象还原》第七章

    <操作系统真象还原>第七章 本篇对应书籍第七章的内容 本篇内容介绍了操作系统的中断处理机制,建立中断描述符表,填充门描述符,以及中断处理程序,初始化8259A中断控制器实现外部中断功能,控 ...

  9. 《操作系统真象还原》第五章 ---- 轻取物理内存容量 启用分页畅游虚拟空间 力斧直斩内核先劈一角 闲庭信步摸谈特权级

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 + 小建议 修改代码前的小闲聊 修改loader.S(读取内存大小) 检验是否成功读取内存大小 开始分页新篇章的分页理解 一级页 ...

  10. 《操作系统真象还原》——2.2 软件接力第一棒,BIOS

    本节书摘来自异步社区<操作系统真象还原>一书中的第2章,第2.2节,作者:郑钢著,更多章节内容可以访问云栖社区"异步社区"公众号查看 2.2 软件接力第一棒,BIOS ...

最新文章

  1. dispatch_queue_create(com.biostime.xxx, DISPATCH_QUEUE_SERIAL)的陷阱
  2. Spring Boot与ActiveMQ的集成
  3. 图解Reformer:一种高效的Transformer
  4. html 模板中的for循环,Flask模板引擎中的For循环
  5. java rest风格传参_SpringMVC的REST风格的四种请求方式总结
  6. [RN] React Native 自定义导航栏随滚动渐变
  7. 微信小程序map作为子组件wx.createMapContext失效
  8. 手把手maven的功能/安装/使用/idea集成
  9. Android studio使用手册说明
  10. mysql查询一个月未登录_mysql函数实例-统计1周内未登陆的用户
  11. alisql mysql_alisql|alisql数据库下载 v5.6 官方版_小皮网
  12. Cypress USB 芯片固件修改,改序列号(Serial Number)
  13. Arduino ide配置esp32硬件支持(配置esp32的arduino开发环境)
  14. 三点共线,向量计算其中一点坐标
  15. Linux下更新Chrome和vscode
  16. 磨金石教育摄影技能干货分享|优秀摄影作品欣赏——世界掠影
  17. 字体图标 fa fa html5,Font Awesome 4.2.0的所有图标参考
  18. CSS边框、边距、轮廓(边框宽度/颜色/各边/简写属性/圆角边框/内外边距/高度宽度/框模型/轮廓宽度/颜色/属性/偏移)——万字长文|一文搞懂
  19. 中国石油大学(北京)-《 完井工程》第三阶段在线作业
  20. 千道Java面试真题整理系列:MySQL灵魂五十问,在遇面试也不怕!

热门文章

  1. 学习笔记: cdq分治
  2. 多线程等待/通知机制
  3. android 教程实例系列
  4. 用c#中的WebBrowser抢小米F码,抢小米手机以及自动测试实现原理
  5. 2018-2019-2 20175223 实验五 《网络编程与安全》实验报告
  6. Jquery监听value的变化
  7. Redis常见配置文件详解
  8. 学习使用autotools
  9. RadioButton 与 XML 之间的协调使用
  10. Delphi中ListView和TreeView的Item中的内存泄露