不满足网上的译文,yqj2065自己翻译一下。备用。
【】中的文字是我的译注、补充。
如果原文链接失效,请google。
概要
在1970s早期,C编程语言是作为新生的Unix操作系统的系统实现语言而设计的。衍生于无类型(typeless)语言BCPL,它进化出了一个类型结构【类型系统】;在弱小的机器上,作为改善简陋编程环境工具的这一创新,它已然成为当今主流语言之一。本文研究它的进化。NOTE: *Copyright 1993 Association for Computing Machinery, Inc. This electronic reprint made available by the author as a courtesy. For further publication rights contact ACM or the author. This article was presented at Second History of Programming Languages conference, Cambridge, Mass., April, 1993.
It was then collected in the conference proceedings: History of Programming Languages-II ed. Thomas J. Bergin, Jr. and Richard G. Gibson, Jr. ACM Press (New York) and Addison-Wesley (Reading, Mass), 1996; ISBN 0-201-89502-1.
本文讲述C程序设计语言的发展、它所受到的影响以及它诞生的条件。简洁起见,我省略了对C自身、它的父亲B[Johnson 73]和祖父BCPL[Richards 79]的完整描述,而是关注于每一种语言的特性要素以及它们是如何演化的。
在1969-1973年间,C的出现,伴随着Unix操作系统的早期发展,而最富创造性的时期是1972年。另外一个(发生)一连串改变的高峰期是1977到1979年间,此时Unix系统的可移植性得以证实。第二个阶段期间,出现了第一份被广为传播的该语言的描述:The C Programming Language,通常被称为‘白皮书’或K&R[Kernighan 78]。最后,在1980年代中期,该语言被ANSI X3J11委员会正式标准化,并作出了进一步的修改。截止1980年代早期,尽管已有各种机器结构及操作系统的【C的各种】编译器,该语言几乎与Unix特别密切关联;更近一些,它的使用传播得更广,而今天它是整个计算机产业中最广泛使用的语言之一。
BCPL编译器在产生输出前,通过存储和分析内存中该完整程序的解析过的表示,可以容易地处理此类构造。B编译器所受的存储限制决定了一种一次通过( one-pass)技术,由此尽可能快生成输出,而这一语法上的重新设计将这种可能带入到C。
BCPL中少量的、归结于技术问题的讨嫌方面,在B的设计中被有意地避免了。例如,BCPL使用一个“全局向量”(global vector)机制用于在分别编译的程序之间通信。在这种模式下、程序员要显式地将每一个外部可见的过程和数据对象的名字,与全局向量中的一个数值偏移量关联起来;被编译的代码通过使用这些数值偏移量完成链接。B避免了这种麻烦,起初是将整个程序一次性地全部提交给编译器。B的后期实现——C也如此,使用一个传统的链接器来解析分别编译的文件中的外部名字,而不是把指定偏移量的负担推给程序员。
从BCPL到B迁移的某些搞法,源于偏好而且存在争议。例如使用单个=字符代替:=表示赋值的决定。还有,B使用/**/括起注释,而BCPL使用//以注释掉直至行末的文本。这显然是PL/I的传统。(C++重新启用了BCPL的注释方式)【C语言也启用了//注释】Fortran影响了声明的语法:B的声明语句先是一个auto或static这种修饰符,后面是名字列表;C不仅遵循这种风格,还把类型关键字放在声明语句的最前面。
并非Richards的书[Richards 79]规范的BCPL与B的每一个不同都是有意的。我们起步于BCPL的一个早期版本[Richards 67]。例如,在我们于1960年代开始学习它时,从 BCPL的switchon语句跳出的endcase并没有出现,因而B和C中都使用的跳离switch语句的关键字break,只能说是殊路同归而非有意的改变。
相对于创建B的过程中产生的诸多语法变化,BCPL的核心语义内容——其类型结构和表达式求值规则——保持不变。两者都是无类型的(typeless),或者说只有单一的数据类型,“字”(word)或“单元”(cell)——一个固定长度的位模式【只有机器字】。这些语言中,内存由这些cell线性数组构成,每一个单元【保存】的内容的含义由所用的操作决定。例如+操作符,使用机器的整数加法指令,将两个操作数简单相加,其它的算术运算也同样不管它们的操作数的实际含义【操作数就是一个01串,不管它是int、char还是位图数据】。由于内存是一个线性数组,就可以将单元的值解释为该数组的索引,而且BCPL为此提供了一个操作符。开始被拼作rv,后为!,而B使用一元*。因此,如果p是这样的单元,保存另一个单元的索引(或地址,或指向它的指针),则*p表示被指向单元的内容,不管*p是作为表达式的值【右值】还是赋值语句的目标【左值】。
因为BCPL和B中指针不过是内存数组的整数索引,对它们进行算术运算是有意义的:如果p是一个单元的地址,那么p+1是下一个单元的地址。这种约定是两种语言中数组的语义的依据。用BCPL编写
let V = vec 10
或用B,
auto V[10];
效果是一样的:命名为V的单元被分配,然后再拔出另一组10个连续单元,而它们中第一个的内存索引被存放在V中。【这不是C的搞法】按照一般规则,B中的表达式 *(V+i)把V和i相加,并指向V后第i个位置。BCPL和B都增加了特别的符号以简化这种对数组的访问;在B中的等价表达式是
V[i]
在BCPL中是
V!i
这种引用数组的方法甚至在当时也不常见;后来C以稍微不同的方式沿用了这一方式。
BCPL,B或C都没有在语言中对字符数据提供强烈的支持;它们都把字符串当作整型的向量[数组]处理,并通过几个约定补充[数组的]通用规则。在BCPL和B中,字符串字面值/文字表示由字符串的字符所初始化的一个静态区的地址,(这些字符)被塞入那些内存单元。BCPL中,第一个被塞的字节包含串所拥有的字符个数;而B,没有该计数而字符串终结于一个特别的字符,在B中被拼写为 `*e'。作出这一改变的部分原因,是避免用一个8位或9位槽(slot)保存串的计数会导致的字符串长度的限制,部分原因是维护该计数似乎,按我们的经验,不如使用一个终结符方便。
BCPL串中单个的字符通常被如此处理:字符串被展开为另一个数组,每单元一个字符,然后再次打包;B提供了相应的例程,但是人们更多地使用其他的库函数以访问和替换一个串中的单个的字符。
续