C语言的前世今生(中)

1972年到1973年这短短的一年是C语言发展最快的时期。由于它是那么优雅和方便,以至于更多的科学家投身其中,其中最为重要的特性是预处理器。Mike Lesk和John Reiser拓展了这项功能,使得预处理指令可以有自己的参数,也允许编译器进行条件判断从而进入不同的编译分支。因此,Ritchie确信到了1973年,现代C语言的核心已经完成,语言本身和编译器都已经成熟,可以在这个夏天为PDP-11重写UNIX内核了。而一年前Thompson刚刚做过一次失败的尝试,那时候的C语言甚至还不支持结构化的数据存储(structures)。C语言变得如此强大,操作系统开发者的信心也随之膨胀,他们开始向其它在当时非常流行的计算机上移植UNIX,比如Honeywell 635和IBM 360/370。也正是从这时起,通用库被开发出来,例如,Lesk写了一个可移植的I/O Package,既后来的标准I/O(standard I/O)。C语言的羽翼日渐丰满。

1978年,Brian Kernighan和Ritchie共同完成了《The C Programming Language 》第一版,其中,Kernighan撰写了几乎全部的解说内容,Ritchie则完成了附录部分,包括参考手册及UNIX开发接口。此后的10年,这本书成为了事实上C的通用标准,也就是大名鼎鼎的"K&R"C。较之移植UNIX时候的C语言,K&R C不但引入了标准I/O库,增加了long int和unsigned int数据类型,还修复了一些语义上的歧义。一个明显的例子是前文提到过的B语言风格的算术赋值运算:a =+ b,假如b是一个字面常量(literal constant),那么问题来了,a =-1究竟应该被理解 成a=a-1还是把-1这个值赋给a呢?于是,C语言正式摒弃了这种操作符组合方式,把算术运算符前置,这样一来,a -= 1和a = -1就没有歧义了。

C语言在移植UNIX上取得了巨大的成功,除了操作系统本身,各种流行的工具软件也被逐个移植到不同的硬件上。70年代初,贝尔实验室的大神级科学家Steve Johnson写了一个叫做"Portable C Compiler(ppc)"的现代编译器,以便他可以把UNIX移植到32位机上。不得不提的是,这位大神也是个爱折腾的主儿,他试图往B语言里面增加"异或"操作符,于是写了个Yacc,在调试代码的时候觉得编译器缺少语义检查能力又随手写了个Lint。在ppc之前,Ritchie写的第一代编译器存在一些"硬代码",目的是针对PDP-11做最佳的优化。但显然给移植到其它机型带来了麻烦。ppc直接使用Yacc生成的语法分析器并且生成更为通用的中间代码。有了这款神器,C语言开始被贝尔实验室以外的组织大规模使用,不但政府的大型软件项目开始用C来编写,甚至在个人计算机上,C语言也成了最受欢迎的开发工具,进入了千家万户。

C语言变得如此流行,到了1982年,它的变体越来越多,但一些扩展数据类型如void和enmu在K&R C中竟然没有提及,在对于实现编译器至关重要的很多细节问题的描述上也模糊不清,显然,K&R C已经无法准确地描述当时C语言的实际情况了。这门最为火爆的语言需要新的标准。1983年夏天,ANSI成立了X3J11委员会,经过漫长的努力,最终在1989年发布了官方标准ANSI X3.159-1989。人们把完全遵循这套标准的C语言称为ANSI C,也叫C89。1990年,国际标准化组织(ISO)采纳了ANSI C作为ISO/IEC 9899:1990,从此C语言有了严格意义上的国际标准,故而C89也可以被称作C90,它们所定义的C语言是完全一样的。标准制定的过程是一场斗争,有很多厂商试图向C语言添加有利于自己软硬件的特性,但ANSI谨小慎微,专家们认为标准应该是保守的,要能兼容大多数旧的基于ppc的编译器而不应该把C语言改得面目全非。于是ANSI C的最终版只做了一处建议性的修改,就是像贝尔实验室科学家Bjame Stroustrup在他所发明的C++中所作的那样,清晰地声明形参(formal arguments)的数据类型。K&R C风格的函数声明不需要指定形参的数据类型,例如:extern int foo();这样的声明只告诉编译器foo是一个返回int类型的函数。而实际上foo的定义可能是下面这样:

在现代C编译器中,int foo();的声明被解释为可以接受任意参数并返回int类型值的函数。于是上面这段代码可以被gcc正常编译而不发出抱怨。但这样做并不好,编译器很难正确理解程序员的意图因而也就不能进行严格的语义检查,代码出错的可能性大大增加了。ANSI并没有宣布旧风格的声明非法,一是它的语法合规且不影响功能;二是这种风格已经延续了十几年,不得不考虑兼容问题。所以直到现在,两种风格的声明都是可以工作的。但几乎所有软件公司的编程规范都禁止使用K&R风格的声明。C89还增加了几个新的限定词(qualifiers),例如"const"和"volatile"。不得不提一下的是,因为众所周知的原因,ANSI C完全基于UNIX环境制定。但是语言本身并没有对访问外部环境的能力,也就是说,库在不同的系统环境下必须被重写。随着库的移植和标准化,1988年,在IEEE 1003基础上出台了"可移植操作系统接口",既POSIX(Portable Operating System Interface),它对应的国际标准是ISO/IEC 9945。

C89的发布标志着C语言正式成为国际通用的编程语言,从此人们可以愉快地同时用K&R和Standard两种风格写程序,编译器会很好地兼容它们。(但不建议这样做,为了避免混淆,最好用__STDC__宏显式地分割。)

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();