点击上方蓝字关注我,了解更多咨询

以我们嵌入式开发中经常使用的C语言为例,我们来介绍一下第一个C语言编译器的来源。

还是让我们回顾一下C语言历史:1970年Tomphson和Ritchie在BCPL(一种解释型语言)的基础上开发了B语言,1973年又在B语言的基础上成功开发出了现在的C语言。在C语言被用作系统编程语言之前,Tomphson也用过B语言编写操作系统。

可见,在C语言实现以前,B语言已经可以投入实用了。因此第一个C语言编译器的原型完全可能是用B语言或者混合B语言与PDP汇编语言编写的。但是B语言的效率比较低,如果全部用汇编语言来编写,不仅开发周期长、维护难度大,更可怕的是失去了高级程序设计语言必需的移植性。

所以,早期的C语言编译器就采取了一个取巧的办法:

先用汇编语言编写一个C语言的一个子集的编译器,再通过这个子集去递推完成完整的C语言编译器。

详细的过程如下:

先创造一个只有C语言最基本功能的子集,记作C0语言,C0语言已经足够简单了,可以直接用汇编语言编写出C0的编译器。依靠C0已有的功能,设计比C0复杂,但仍然不完整的C语言的又一个子集C1语言,其中C0属于C1,C1属于C,用C0开发出C1语言的编译器。在C1的基础上设计C语言的又一个子集C2语言,C2语言比C1复杂,但是仍然不是完整的C语言,开发出C2语言的编译器……如此直到CN,CN已经足够强大了,这时候就足够开发出完整的C语言编译器的实现了。

至于这里的N是多少,这取决于你的目标语言(这里是C语言)的复杂程度和程序员的编程能力。简单地说,如果到了某个子集阶段,可以很方便地利用现有功能实现C语言时,那么你就找到N了。

下面的图说明了这个抽象过程:

这张图是不是有点熟悉?对了就是在将虚拟机的时候见到过,不过这里是CVM(CLanguageVirtualMachine),每种语言都是在每个虚拟层上可以独立实现编译的,并且除了C语言外,每一层的输出都将作为下一层的输入(最后一层的输出就是应用程序了)。

这和滚雪球是一个道理。用手(汇编语言)把一小把雪结合在一起,一点点地滚下去就形成了一个大雪球,这大概就是所谓的0生1,1生C,C生万物吧?

那么这种大胆的子集简化的方法,是怎么实现的,又有什么理论依据呢?

先介绍一个概念,“自编译”(Self-Compile),也就是对于某些具有明显自举性质的强类型(所谓强类型就是程序中的每个变量必学声明类型后才能使用,比如C语言,相反有些脚本语言则根本没有类型这一说法)编程语言,可以借助它们的一个有限小子集,通过有限次数的递推来实现对它们自身的表述,这样的语言有C、Pascal、Ada等等,至于为什么可以自编译,可以参见清华大学出版社的《编译原理》,书中实现了一个Pascal的子集的编译器。总之,已经有CS科学家证明了,C语言理论上是可以通过上面说的CVM的方法实现完整的编译器的,那么实际上是怎样做到简化的呢?

下面是C99的关键字:

//共37个
auto enum restrict unsigned
break extern return void
case float short volatile
char for signed while
const goto sizeof _Bool
continue if static _Complex
default inline struct _Imaginary
do int switch double long typedef
else register union

仔细看看,其中有很多关键字是为了帮助编译器进行优化的,还有一些是用来限定变量、函数的作用域、链接性或者生存周期(函数没有)的,这些在编译器实现的早期根本不必加上,于是可以去掉

auto,restrict,extern,volatile,const,sizeof,static,inline,register,typedef,这样就形成了C的子集,C3语言,C3语言的关键字如下:

//共27个
enum unsigned break return
void case float short char
for signed while goto _Bool
continue if _Complex default
struct _Imaginary do int switch
double long else union

再想一想,发现C3中其实有很多类型和类型修饰符是没有必要一次性都加上去的,比如三种整型,只要实现int就行了,因此进一步去掉这些关键词,它们是:

unsigned,float,short,char(charisint),signed,_Bool,_Complex,_Imaginary,long,

这样就形成了我们的C2语言,C2语言关键字如下:

//共18个
enum break return void
case for while goto continue
if default struct do int
switch double else union

继续思考,即使是只有18个关键字的C2语言,依然有很多,高级的地方,比如基于基本数据类型的复合数据结构,另外我们的关键字表中是没有写运算符的,在C语言中的复合赋值运算符->运算符等的++,--等过于灵活的表达方式此时也可以完全删除掉,因此可以去掉的关键字有:enum,struct,union,这样我们可以得到C1语言的关键字:

break return void case for while
goto continue if default do int
switch double else
//共15个

接近完美了,不过最后一步手笔自然要大一点。这个时候数组和指针也要去掉了,另外C1语言其实仍然有很大的冗杂度,比如控制循环和分支的都有多种表述方法,其实都可简化成一种,具体的来说,循环语句有while循环,do...while循环和for循环,只需要保留while循环就够了;分支语句又有if...{},if...{}...else,if...{}...elseif...,switch,这四种形式,它们都可以通过两个以上的if...{}来实现,因此只需要保留if,...{}就够了。可是再一想,所谓的分支和循环不过是条件跳转语句罢了,函数调用语句也不过是一个压栈和跳转语句罢了,因此只需要goto(未限制的goto)。

因此大胆去掉所有结构化关键字,连函数也没有,得到的C0语言关键字如下:

//共5个
break voidgoto int double
  • 1.

  • 2.

这已经是简约的极致了。

只有5个关键字,已经完全可以用汇编语言快速的实现了。通过逆向分析我们还原了第一个C语言编译器的编写过程,也感受到了前辈科学家们的智慧和勤劳!我们都不过是巨人肩膀上的灰尘罢了!

0生1,1生C,C生万物,实在巧妙!

任何一种语言的第一个编译器肯定是使用其他语言写出来的。那就有一个问题了:第一种语言的第一个编译器是怎么来的呢?哈哈!

END

*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

戳“阅读原文”我们一起进步

第一个C语言编译器是怎样编写的?相关推荐

  1. 第一个C语言编译器是怎样编写的

    点击蓝字 关注我们 因公众号更改推送规则,请点"在看"并加"星标"第一时间获取精彩技术分享 来源于网络,侵删 不知道你有没有想过,某种编程语言的第一个编译器是怎 ...

  2. 学习较底层编程:动手写一个C语言编译器

    动手编写一个编译器,学习一下较为底层的编程方式,是一种学习计算机到底是如何工作的非常有效方法. 编译器通常被看作是十分复杂的工程.事实上,编写一个产品级的编译器也确实是一个庞大的任务.但是写一个小巧可 ...

  3. 手写一个C语言编译器

    记录一个很好的C语言编译器项目地址 手把手教你构建 C 语言编译器(0)- 前言 | 三点水

  4. 第一个C语言程序怎么开始编写(全过程简单易懂 )

    在之前看网上的教程下载好visual statio. 第一步:win10系统找到你下载的这个软件单击(visual statio 2019) 第二步:点击创建新项目 第三步:点击空项目 第四步:输入项 ...

  5. keil c语言编译运行,Keil的c语言编译器

    我曾经通过查看反汇编代码对KEILC编译器进行了测试,大概有这么一下内容,也得出一些结论. (1)全局变量:如果程序中定义了全局变量,而且初始值不是0.此时,在程序调到main()函数执行前,除了要进 ...

  6. C语言这么厉害,它自身又是用什么语言写的?编写过程被称为自举!

    来自一个小白的提问:"C语言本身用什么语言写的?" 换个角度来问,其实是:C语言在运行之前,得编译才行,那C语言的编译器从哪里来?用什么语言来写的?如果是用C语言本身来写的,到底是 ...

  7. c语言编译器怎窗口怎么执行,c语言编译(c语言编译执行详解)

    大家一般都用的是什么软件呢? 1.C语言编程软件有哪些:Mcrosoft Visual C++ .Microsoft Visual Studio. DEV C++.Code::Blocks.Borla ...

  8. c4c语言编译器,c4编译器源码剖析

    c4_source 1. 概述 c4是500行代码实现一个c语言编译器 简单暴力,适合了解基础的编译器原理 1 2 3 2. 主流程 1.建立系统符号表 2.读取源代码 3.一次遍历源代码,同时进行词 ...

  9. 用c语言编写一个简易的编译器,面向教学的简易c语言编译器的设计与实现(54页)-原创力文档...

    目录 TOC \o "1-5" \h \z \o "Current Document" 摘要I ABSTRACTII \o "Current Docu ...

最新文章

  1. 普通程序员,几个月如何成功转型AI?
  2. k-means聚类分割
  3. Selective Search
  4. 芝麻信用_别忘查看支付宝芝麻信用分,这些特权别浪费
  5. 二 用标准c语言实现hanoi塔问题,天大2016年1二月《数据结构》期末大作业考核要求.doc...
  6. python字符串格式化_Python3 字符串格式化
  7. SSH抛出org.apache.ibatis.exceptions.PersistenceException: 异常
  8. css 浮动 相对定位 绝对定位区别
  9. Paxos算法在大型系统中常见的应用场景
  10. JAVA网站后台管理系统
  11. UG NX 12 抽取面特征
  12. “两地三中心”和“双活”简介--容灾技术方案
  13. 学计算机专业工作总结,计算机专业学生个人实习工作总结范文
  14. 华为手机鸿蒙切换主页,京东APP可一键切换“华为鸿蒙版界面”:简洁多了
  15. Android init.rc介绍及其语法
  16. CAD能打开PDF格式吗?这样做可以快速实现
  17. Mybatis常见面试问题(附答案)
  18. Mac 终端——常用命令语
  19. matplotlib无法显示中文问题的解决(全网最全)
  20. macbookpro安装ch340驱动

热门文章

  1. 微电子新手入门之Cadence常用操作——ADS电磁仿真后电感的SP参数文件导入到Cadence中
  2. vue3 分析vue脚手架工程结构
  3. OneKey一键还原8.1.0.314(支持固态硬盘和动态磁盘GPT分区)官方免费版软件
  4. Cell Biolabs——细胞研究丨细胞信号通路和蛋白质生物学
  5. 22考研西安交大915编程题
  6. Elasticsearch中flattened字段类型
  7. Excel VBA 插入指定图片到单元格并只适应大小
  8. 如何手动关闭elementUI的popover
  9. 关于ElementUI的el-popover的问题:文本换行、修改样式
  10. 企业工商信息查询标准版API开发文档