设为首页 加入收藏

TOP

C语言的发展史(The Development of the C Language) (四)
2014-11-23 22:19:18 来源: 作者: 【 】 浏览:4
Tags:语言 发展史 The Development the Language
术问题?


这个解决方案形成了一个,在无类型BCPL和类型化C之间进化链中的重要飞跃。它移除了指针在内存的具体化。相反促成数组名出现在表达式中时生成指针。C语言的这个规则一直存在至今,就是数组类型的值当出现在表达式中时,被转换成指向组成数组的对象中的第一个对象的指针。


这个发明使现存的B代码能继续工作,不管下层语言语义的改变。仅有几个程序为了调整它的起点,把新值赋给数组名——在B和BCPL中是可能的,在C中无意义——都被很容易修改。更重要的是,新语言保持一致性和对数组有效的(如果并非常见)可解释性,开辟了通往复杂类型结构的道路。


第二个创新,极力明显地把C与它的前辈们区分开来,那就是更完整的类型结构,尤其是在声明语法中的表达式,NB提供基本的类型int和char,它们的数组,指向它们的指针,但没有更进一步的组合。通用化也被要求:给定一个任意类型对象,描述一个包含它们的新对象,从一个函数求得它,或一个指向它的指针,都是可能的。


对每一个此类复合类对象,已经有了一种讨论下层对象的方式:索引数组,调用函数,间接引用指针。类比推理导致了一种名字镜像声明语法,名字特征出现的表达式语法,因此,


int i, *pi, **ppi;
声明一个整数,一个指向整数的指针,一个指向指向整数的指针的指针。这些声明的语法反映i,*pi和**pi用于表达式时,都得到一个整数类型。类似地,


int f(), *f(), (*f)();
声明一个返回整型值的函数,一个返回整型指针的函数,一个指向返回整型函数的指针;


int *api[10], (*pai)[10];
声明一个整型指针数组,一指向整型数组的指针。在所有这些情况中,一个变量的声明类似它在表达式中的用法,它的类型是在声明中,置于开头的那个。


C语言采用类型组合模式归功于Algol 68,尽管它或许没有以Algol追随者认可的模式出现。我从Algol获取的主要概念,是一个基于原子类型的类型结构(包括结构),组合为数组,指针(引用),和函数(过程)。Algol 68关于union和转换的概念的影响,在后来也表现出来。


创造类型系统之后,我认为这些相关的语法,新语言的编译器需要一个新名字。NB看起来不够有自己的特点。我决定延用单字母风格并取名为C,而并没有肯定答复,关于名字是否表示字母表或是BCPL中的字母顺序的问题。


C初生


在语言取名之后,其它改变很快在进行,例如引入||和&&操作符。在BCPL和B中,表达式求值依赖于上下文:if和其它条件语句内,把一个表达式的值与零比较,这些语言对与(&)和或(or)运算符会给与特别解释。在普通上下文中,它们进行按位运算,但在这个B语句中


if (e1 & e2) ...
编译器必须对e1求值并且如果它是非零值,对e2求值,并且如果它也是非零,则执行依赖if的语句。在e1和e2内的&和|运算符的求值要求,以此类推。此类真值上下文中的布尔运算符的短路(short-circuit)语义被期望,但是运算符的过度使用,难以解释和运用。在Alan Snyder的建议下,我提出&&和||运算符,以使这种机制更直接。


它们姗姗来迟的出现,说明了C语言中不合适的优先级规则。一个人在B中这样写


if (a==b & c) ...
来检测是否a和a相等并且c是否非零;在这样的条件表达式中,&比==的优先级低就好得多。在从B转变为C时,一个人想在这中表达式中,用&&代替&;为了使这种转换不那么痛苦,我们决定保持&运算符与==有相等的优先级,仅仅把&&的优先级与&的做了细微区分。今天,看起来变动&和==的相对优先级会更好,这样就能简化一个C通用的惯用法:为了测试一个掩码值和另一个值,人们必须这样写


if ((a&mask) == b) ...
那个内层的圆括弧是需要的,但会被容易忘记。


许多其它改变发生在1972-3年,但是最重要的是引入了预处理器,部分是因为Alan Snyder[Snyder 74]的催促,但也是为了承认在BCPL和PL/I中已存在的文件包含机制。它的原始版本极之简单,仅提供文件包含和简单字符串替换:#include和参数化宏#define。之后很快对它进行了扩展,主要是Mike Lesk的工作,后来是John Reiser,合并了宏与参数还有条件编译。预处理原本是做为语言本身的一个辅助手段。确实,这么多年来,它甚至未被调用除非在源文件的开始处包含了一个特别信号。这种看法一直持续,解释了在早期参考手册中,预处理器语法与语言其它部分的不完整整合,和对它的不精确描述。


可移植性


在1973年早些时候,现代C的基础部分已经完成。在那年夏天,语言和编译器已足够强壮,以允许我们在PDP-11上用C重写Unix内核。(Thompson已用C的早期版本,作了一个生成系统代码的简略尝试——在结构类型出现之前——1972年时,但是放弃了那次努力。) 也是在这段时期,编译器被转向了其它临近的机器,特别是Honeywell 635和IBM 360/370;因为语言不能独自存在,现代库原型已被开发。特别是Lesk写了一个后来被修改成为C的“标准I/O”例程的“可移植I/O包”[Lesk 72]。在1978年,Brian Kernighan和我出版了C程序设计语言[Kernighan 78]。尽管这本书没有描述一些添加的,在后来变成通用的特性,它充当了语言标准,直到一个正式的标准在十多年后被采纳。尽管我们在本书上关系密切,在工作中有着很明显的区别:Kernighan编写几乎所有的解释性的内容,我负责参考手册中包含的附录,和与Unix系统的接口的那一章。


在1973-1980年间,语言又有了一些发展:类型结构有了unsigned, long, union和枚举类型,并且结构几乎成为第一类对象(缺少一个字面值符号)。在它的环境和其它伴随技术中,也产生了同等重要的发展。用C编写Unix内核,让我们对语言的有用性、效率有足够的信心,我们开始重新编码系统的应用程序和工具,并且随后把其中最有趣的东西移到其它平台。像在[Johnson 78a]中描述的那样,我们发现传播Unix工具的最困难的问题,并不在C语言和新硬件的接口,而是在适应其它操作系统上的现有软件。因此Steve Johnson开始了在pcc上的工作,一个可被容易移植到新机器上去的C编译器[Johnson 78b],在他,Thompson和我开始把Unix系统移到Interdata 8/32计算机的时候。


语言在这时期的改变,特别是1977年左右,很大程度上集中在可移植性和类型安全的考虑,是为了努力处理我们预见和观察到的,在相当多的代码移到新的Interdate平台产生的问题。C在那时仍表现很强的无类型根源的迹象。例如,在早期语言手册或存在的代码中,指针很少被与整型内存索引区分开来;字符指针的算术特性和无符号整数的相似性,使抵制识别它们的尝试很难。无符号类型被添加进来使无符号算术有效,不让它与指针操作混淆在一起。类似地,早期语言容许了整数与指针间的赋值,但这个实践并未开始得到鼓励;一个类型转换的符号(在Algol 68的例子中被称作“casts”)被发明,以更明显指示类型转换。由于PL/I例子的诱惑,早期C没有强烈绑定结构指针与它们所指向的结构,并且允许程序员用

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 4/8/8
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言中异常处理的两个函数 下一篇位运算超强总结

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: