C语言是家喻户晓的语言,即使没有学过编程语言的人也知道它的名字。它与我们一起走过了48年的沧桑岁月。回首那一年,C语言还处于起步阶段……贝尔实验室特殊人才奖、美国计算机协会(ACM)图灵奖、汉明奖章、计算机先驱奖、计算机历史博物馆、HaroldPender奖……这些成就都来自于一个人,那就是编程界无人能及的传奇人物、C语言的缔造者——DennisRich。C语言之父:DennisRitchie计算机历史学家PaulE.Ceruzzi说:Ritchie不为人所知。他的名字一点也不家喻户晓,但如果你有显微镜,可以在电脑上看到他的作品,你就会发现他的作品无处不在。克尔尼汉也这样评价:“牛顿说他是站在巨人的肩膀上,今天,我们都是站在里奇的肩膀上。”01C语言的辉煌历史1941年,丹尼斯·里奇出生在纽约州布朗市的克里斯,他的父亲是贝尔实验室的交换系统工程师。里奇从小成绩优异,顺利进入哈佛大学。在父亲的影响下,丹尼斯也走上了科研之路。在哈佛求学期间,一个偶然的机会改变了里奇的人生。里奇在哈佛参加了计算机系统相关的讲座,从那以后他就对计算机着了迷,不仅上了专门的课程。当时,里奇是一名物理专业的学生。因为他对计算机处理的理论和实际问题非常着迷,所以他的毕业论文大多是与计算机理论(递归函数的层次)相关,这还远远不够。Rich开始花更多的精力在练习上。在大多数计算机体积庞大、占据整个房间、拨号访问受限的时代,工程师们的目标是征服小型台式计算机,但这些计算机没有易于使用的操作系统,所以里奇决定自己做一个。这一决定立即得到麻省理工霍尼韦尔和通用电气的支持。Rich负责多处理器BCPL语言和GE650编译器,两者都属于GECOS系统。同时,他还为符号计算机的语言和系统ALTRAN语言编写了代数编译器。完成这个项目后,里奇毅然放弃了自己的物理专业,决定以计算机为职业。1967年加入贝尔实验室。加入贝尔实验室后,Rich开始与实验室的一位名叫肯·汤普森(KenThompson)的成员一起工作。这位肯汤普森也是对里奇的职业生涯影响很大的人。Thompson和Ritchie在70年代,Thompson和Ritchie正在研究如何让早期的小型计算机越来越流行。他们认为,需要的是各种计算机之间更简单、更可行的交互。因为较旧的计算机要求用户使用操作系统来复制、删除、编辑和打印数据文件,将数据从磁盘移动到屏幕再到打印机再返回磁盘进行存储。没有操作系统,除了少数专家外,任何人都无法访问计算机。为此,他们花了几个月的时间才想出一个解决方案,到完成时他们已经写出了影响他们生活的Unix操作系统。Rich在1999年的一次采访中说:“我认为Linux发展的现象是喜人的。虽然工作站和大型计算机制造商也在提供不同种类的BSD系统,但在Unix的直接衍生产品中,Linux应该是最流行的。声音。“C++开发人员和设计师BjarneStraussup曾说过:“如果Rich决定在那十年中将他的精力花在奇怪的数学上,那么Unix就会死掉。事实上。”加入贝尔实验室后,Rich开发了C语言和Unix系统,两者在计算机工业史上都占有重要地位。C++、C#、Objective-C、Java、JavaScript等语言影响很大。UnivacI为了在PDP-11计算机上运行Unix系统,1972年,贝尔实验室的DennisMacAlistairRitchie在美国在B语言的基础上设计了C语言。C最初试图通过在B中加入数据类型的思想来处理那些不同类型的数据。在C中,和大多数编程语言一样,每个对象都有一个类型作为well作为一个值;类型决定了对该值可用的操作的含义,以及对象占用了多少存储空间。1973年,肯·汤普森(KenThompson)和里奇合作用C语言重写了90%以上的Unix,也就是第五个ediUnix的重刑。这是C语言首次用于操作系统的核心编程。随着Unix的使用越来越多,C语言也得到了迅速的推广。Unix最初是用汇编语言编写的。Rich和Thompson将其重写并于1974年发表在ACM上,正式对外公开了Unix系统。随着Unix的发展,C语言也得到了不断的完善。C语言是一种面向过程的、抽象的编程语言,广泛应用于底层开发。C语言可以很容易地编译和处理低级内存。如此简单、如此干净,以至于几乎每个计算机制造商都转向了它,并产生了戏剧性的效果。为了促进C语言的全面推广,众多专家学者和硬件厂商共同组成了C语言标准委员会。于是在1989年,第一个完整的C标准诞生了,简称“C89”。时至今日,最新的C语言标准是2017年发布的“C17”。虽然C语言如日中天,但Rich的职业生涯并没有就此结束。1990年,他成为朗讯科技公司计算技术研究部的负责人。在此职位上,他编写应用程序并管理已发布操作系统的发展。1975年,C语言开始移植到其他机器上。StephenC.Johnson实现了一套“便携式编译器”,这些编译器相对容易修改并且可以为不同的机器生成代码。从那时起,C语言就被用于大多数计算机,从最小的微型计算机到CRAY-2超级计算机。C语言非常规范。即使没有正式的标准,也可以编写C程序。这些程序无需修改即可在任何支持C语言最小运行环境的计算机上运行。《C程序设计语言》的第一版于1978年由DennisRich和BrianWilsonKernighan出版。书中介绍的C语言标准也被C语言程序员称为“K&RC”(CurleyC),本书第二版也收录了一些ANSIC标准。即使在ANSIC标准提出多年后,K&RC仍然是很多编译器的最低标准要求,很多老的编译器仍然运行K&RC标准。1978年后,C语言相继移植到大、中、小型和微型计算机上。C语言迅速风靡全球,成为世界上使用最广泛的高级程序设计语言。C最初是在小型机器上实现的,继承了一系列小程序语言的特点;C的设计者更喜欢简单和优雅而不是功能。此外,C从一开始就是为系统级编程而设计的,程序运行的效率非常关键,因此C与真实机器的能力非常匹配也就不足为奇了。例如,C语言为典型硬件直接支持的对象提供了相应的基本数据类型:字符、整数(可能有各种大小)和浮点数(也可能有多种大小)。1983年,里奇和汤普森因发展通用操作系统理论和实现UNIX操作系统而获得图灵奖。里奇的图灵奖论文题目是《对软件研究的反思》。1989年,C语言被美国国家标准协会(ANSI)标准化,编号为ANSIX3.159-1989。此版本也称为C89。标准化的目的之一是通过添加一些新功能来扩展K&RC。1990年,国际标准化组织(ISO)成立了ISO/IECJTC1/SC22/WG14工作组来规定国际标准C语言。通过对ANSI标准的少量修改,最终制定了ISO9899:1990,也就是大家熟知的C90。随后,ANSI也接受了国际标准C,不再制定新的C标准。ANSI标准制定后,C语言的规范在一段时间内没有发生太大的变化,但C++在创建自己的标准化过程中不断发展壮大。《标准修正案一》在1994年为C语言创建了一个新的标准,但只是修复了C89标准中的一些细节,并增加了更多更广泛的国际字符集支持。然而,这个标准导致了ISO9899:1999在1999年的发布。它通常被称为C99。C99于2000年3月被ANSI采用。1990年,还是孩子的两人因“创造了UNIX操作系统和C编程语言”而获得了IEEE颁发的IEEEHamming奖。1997年,他们被授予计算机历史博物馆研究员奖。成就奖以表彰他对计算机科学技术的贡献,以及UNIX操作系统对社会的广泛影响。2011年,Rich和Thompson分享了日本国际奖。但在2011年10月12日,Rich离开了这个世界,离开了他为之倾注毕生精力的C语言和Unix世界,在他70岁高龄之际,去了另一个地方,开始了他的另一段旅程……2011年12月8月8日,ISO正式发布了新C语言的新标准C11,之前称为C1X,正式名称为ISO/IEC9899:2011。新标准提高了与C++的兼容性并添加了一些新功能。这些新功能包括通用宏、多线程、边界检查函数、匿名结构等。C18(以前称为C17)是C语言编程的最新标准,于2018年6月发布以取代C11。C18在不引入新语言功能的情况下解决了C11中的不足。由于C具有简洁紧凑的语言,使用起来方便灵活。运算符,丰富的数据类型;结构化控制语句,语法限制较少,程序设计自由度大;C语言允许直接访问物理地址,可以进行位操作,可以实现汇编语言的大部分功能,可以直接对硬件进行操作;生成高质量的目标代码。执行效率高等。因此,虽然C语言发布已经很多年了,但C语言在某些领域仍然很受欢迎。目前,C语言编译器普遍存在于各种操作系统中,如MicrosoftWindows、macOS、Linux、Unix。C语言的设计影响了后来的很多编程语言,如C++、Objective-C、Java、C#等。02C语言能做什么?计算机发展至今,编程语言层出不穷,但无论涌现出多少“新人”,都无法改变C语言在编程界备受推崇的地位。C语言能做多少事?人们常说Linux操作系统的内核是用C语言编写的,而很多相应的嵌入式内核驱动都跑不出C语言的范畴,包括常用的手机、机顶盒、电视等。底层硬件驱动基本都是用C语言完成的。毫不夸张地说,如果没有C语言,就没有微软的Windows10和SurfaceBook,就没有安卓智能手机,更没有乔布斯一手打造的苹果帝国的各种产品Mac和iPad。C语言最棒的地方在于,几乎所有的底层语言对上层语言的处理,大多都是由C语言大佬作为嫁衣完成的。深入理解上层语言的底层实现,离不开C语言。而且很多大学的计算机专业都会把C语言作为学生编程入门的第一步。因此,很多程序员都把学习C语言作为自己编程生涯中最基础的事情。而C语言之所以能够成为最重要、最流行的编程语言之一,具体有以下几个原因:设计特点C语言结合了计算机科学理论和实践的控制特点。C语言的设计理念使用户可以轻松完成自上而下的规划、结构化编程和模块化设计。因此,用C编写的程序更容易理解,也更可靠。效率在设计上,它充分利用了现在计算机的优点,所以C程序相对更紧凑,运行速度更快。可移植性C是一种可移植的语言。这意味着在一个系统上编写的C程序只需很少修改或无需修改即可在另一个系统上运行。如需修改,只需要在主程序的头文件中简单改动几项即可。强大而灵活C语言强大而灵活。一个强大而灵活的UNIX操作系统,主要用C语言编写。C程序还可以用来解决物理和工程问题,甚至可以为电影制作动画特效。对于程序员C语言旨在满足程序员的需要,他们使用C来访问硬件和操作内存中的位。C语言有丰富的运算符,可以让程序员简洁地表达自己的意图。03C语言是怎么来的C语言是一种非常低级的语言,在很多方面都类似于汇编语言。在《Intel 32位汇编语言程序设计》这本书中,甚至介绍了将简单的C语言手动翻译成汇编语言的方法。对于编译器这样的系统软件,自然要用C语言来写,即使是Python这样的高级语言,底层还是要依赖C语言(Python就是一个例子,因为Intel黑客试图让Python不需要操作系统只是运行-有效地消除了BIOS上的一次性C代码)。现在的学生,在学习了编译原理后,只要有一点编程能力,就可以实现一个功能简单的类C语言编译器。但问题来了。不知道大家有没有想过。大家都用C或基于C的语言写编译器,那么世界上第一个C语言编译器是怎么写出来的呢?这不是一个“先有鸡还是先有蛋”的问题……上面也提到了第一个C语言编译器的原型可能是用B语言写的,也可能是B语言和PDP汇编语言混合写的。早期的C语言编译器把取巧的做法:先用汇编语言为C语言的一个子集写一个编译器,然后通过这个子集递归完成一个完整的C语言编译器。详细过程如下:先创建一个只有最基本功能的子集C语言,简称C0语言,C0语言足够简单,可以直接用汇编语言写出一个C0编译器,依托C0已有的功能,设计比C0复杂,但还是不完善的C1语言C语言的另一个子集,其中C0属于C1,C1属于C,用C0开发了C1语言的编译器,在C1的基础上设计了C语言的另一个子集C2语言。比更复杂C1,但它仍然不是一个完整的C语言。开发了一个C2语言的编译器……所以直到CN,CN已经足够强大了,这个时间足以开发出一个完整的C语言编译器实现。至于这里的N是什么,取决于你的目标语言(这里是C)的复杂程度和程序员的编程能力——总之,如果你到了某个子集阶段,你可以很容易地使用已有的函数实现C时,那么你已经找到N了。下图说明了这个抽象过程:那么这种大胆的子集简化方法是如何实现的,其理论依据是什么?先介绍一个概念,“自编译”Self-Compile,即对于一些具有明显自举特性的强类型(所谓强类型是指程序中的每个变量都必须声明一个类型才能使用,比如C语言,相反,一些脚本语言根本没有类型)编程语言,你可以使用它们有限的小子集通过有限的递归来表达自己。这类语言有C、Pascal、Ada等,至于为什么可以自编译,可以参考清华大学出版社的《编译原理》,该书实现了CompilerforasubsetofPascal。总之,有计算机科学家已经证明,C语言理论上可以通过上述CVM方法实现一个完整的编译器,那么在实践中如何简化呢?这画面是不是有点眼熟?顺便说一下虚拟机的时候看到的,不过这里是CVM(CLanguageVirtualMachine),每一种语言都可以在每一个虚拟层上独立编译,而且除了C语言,每一层的输出都会作为下一层的输入(最后一层的输出就是应用程序),跟滚雪球一样。用手(汇编语言)组合一小把雪,然后一点点滚下去,形成一个大雪球。这大概就是所谓的0生1,1生C,C生万物吧?下面是C99Word的key:autoenumrestrictunsignedbreakexternreturnvoidcasefloatshortvolatilecharforsignedwhileconstgotosizeof_Boolcontinueifstatic_Complexdefaultinlinestruct_Imaginarydointswitchdoublelongtypedefelseregisterunion//仔细看一共37个关键字,其实很多都是为了帮助编译器优化,还有一些是用来限制生命周期的,函数不带变量,函数链接)是的,这些在编译器实现的前期是不需要加的,所以auto、restrict、extern、volatile、const、sizeof、static、inline、register、typedef可以去掉,这样就形成了C、C3的子集language,C3语言的关键字如下:enumunsignedbreakreturnvoidshortcharforsignedwhilegoto_Boolcontinueif_Complexdefaultstruct_Imaginarydointswitchdoublelongelseunion//C3中一共27种类型和类型修饰符不用一下子全部加上,比如整型三种,只要实现了int即可,So进一步重新移动这些关键字,它们是:unsigned,float,short,char(charisint),signed,_Bool,_Complex,_Imaginary,long,这样就形成了我们的C2语言,C2语言关键字如下:enumbreakreturnvoidcaseforwhilegotocontinueifdefaultstructdointswitchdoubleelseunion//一共18个继续想,即使C2语言只有18个关键字,但还是有很多高级的地方,比如基于基本数据类型的复合数据结构,而我们的关键字列表中没有关键字列表写运算符,复合赋值运算符->、运算符++、-等C语言中过于灵活的表达式此时也可以完全删除,所以可以删除的关键字有:enum、struct、union,所以我们可以得到C1语言的关键字:breakreturnvoidcaseforwhilegotocontinueifdefaultdointswitchdoubleelse//一共15个接近完美,最后一步自然更大了。这时候数组和指针就得去掉。另外,C1语言还有很多冗余,比如控制循环和分支的表达方式有很多种,但是都可以简化为一个。具体来说,循环语句包括while循环、do...while循环和for循环,保留while循环即可;分支语句有if...{},if...{}...else,if...{}...elseif...,switch,这四种形式可以用两个以上的if实现...{},所以只需要保留if,...{}就足够了。但是再想一想,所谓的分支和循环只是条件跳转语句,而函数调用语句只是压入和跳转语句,所以只需要goto(无限制的goto)即可。所以大胆去掉所有的结构关键字,连函数也不行,得到的C0语言关键字如下:breakvoidgotointdouble//一共5个,已经是极致的简洁了。关键字只有5个,用汇编语言可以快速实现。通过逆向分析,我们还原了第一个C语言编译器的编写过程,也感受到了前辈们的智慧与辛劳!我们都只是巨人肩上的尘土!0生1,1生C,C生万物,真巧妙!
