设为首页 加入收藏

TOP

C语言的发展史(The Development of the C Language) (三)
2014-11-23 22:19:18 来源: 作者: 【 】 浏览:6
Tags:语言 发展史 The Development the Language
析的第一种形式的迷惑捷径导致的错误,在1967年被修复。)


Thompson通过发明自增++和自减--运算符,走出了更深远的一步;它们的前缀或后缀位置决定变更是发生在计算运算对象值之前或之后。它们没有出现在B的最早版本中,而是随后才出现的。人们经常猜测,它们被创造是为了使用,C和Unix在其上首次流行的DEC PDP-11提供的自增和自减地址模式。这在历史上来说是不可能的,因为B被发明的时候还没有PDP-11。PDP-7有一些“自增”内存单元,使用这种特性,一个间接内存引用通过它们来自增单元。这些特征可能提示Thompson创造了那些自增运算符;他把前缀和后缀一般化。甚至,自增单元没有被直接用于实现这些运算符,并且这种创新一个更强烈的动机可能是,他发觉++x的翻译在尺寸上小于x=x+1。


PDP-7上的B编译器不产生机器指令,而是一个由编译器输出组成代码段地址序列,执行基本运算的解释模式的threaded代码[Bell 72]。这些操作——特别对B——典型地运行在一个简单堆栈机器上。


在PDP-7的Unix系统上,除了B本身只有几个东西是B写的,因为这个机器太小和太慢,除了试验而不能做更多事情;完全用B重写操作系统和其它应用程序,是看起来不可行的代价高昂的动作。Thompson在某些地方,通过提供一个利用换页解释器代码和数据,允许解释超过8K字节的程序的“虚拟B”编译器,来释放地址空间,但它对通用程序来说太慢以致不实用。尽管如此,一些用B写的工具还是出现了,包括一个早期版本的,Unix用户熟悉的可变精度计算器dc[McIlroy 79]。我做的最有雄心壮志的工作,是一个把B翻译为GE-635机器指令而非threaded 代码的真正的交叉编译器。它是一个精巧的绝技:一个用本身语言写的,生成在一个,在有4k字长用户地址空间的18位机器上运行的36位大型机代码,完全的B编译器。这个项目能实现,仅仅是因为B的简单性和它的运行时系统。


尽管我们抱有关于实现一个那时,像Fortran, PL/I或Algol 68的主要语言的偶然想法。这样的项目对我们的显得绝望的大:需要更简单和小的工具。所有这些语言都影响我们的工作,但是凭我们自己之力来做这些事情则更有趣。


到1970年时,我们看起来能在Unix项目上,获得一个新的DEC PDP-11。处理器是DEC递交的第一批产品,三个月后,磁盘才到达。通过threaded技巧,使B程序在其上运行只需要为运算符重写代码段,和一个我用B写的简单的汇编器。很快,dc成了在其它操作系统之前,第一个在我们的PDP-11上被测试的有趣的程序。几乎非常快,但仍需等待磁盘,Thompson用PDP-11汇编语言,重写了Unix内核和一些基本命令。最早的PDP-11上的Unix把机器上24K内存中的12K给操作系统,一个很小的空间给用户程序,其余的作为RAM磁盘。这一版本仅是用于测试,而不是实际的工作;这个机器通过枚举关闭的,knight的不同尺寸象棋板的路程,来标记时间。在磁盘到达后,我们把汇编语言转换为PDP-11上的方言,和移植一些B程序,很快移植到它上面去。


到1971年时,我们的微型计算机中心开始有了用户。我们都希望更容易编写有趣的软件。使用汇编显得沉闷,B不管它的性能问题,已经有了一个小的包含有用服务例程的库,并且被用于越来越多的新程序。这段时期的最著名的成果,是Steve Johnson的yacc分析——生成器[Johnson 79a]的第一个版本。


B的问题


我们第一次使用BCPL然后是B的机器,是按字寻址的,这些语言的单一数据类型,“单元”,能恰当与硬件机器字互相换算。PDP-11的出现暴露了B的语义模型的一些不足。首先,它从BCPL继承的几乎未作改变的字符处理机制是笨拙的:使用库方法把包装的字符串展开到单个的单元,然后再次包装,或者访问或替换单个字符,在一个面向字节的机器上,开始变得笨拙,甚至愚蠢。


其次,尽管最初的PDP-11没有提供浮点算术运算,制造商承诺将很快提供。浮点运算通过定义特别的运算符,被添加到我们的Multics和GCOS的B编译器,但是这种机制仅在相应的机器上才可能,单个字长足够包含一个浮点数;这在16位PDP-11上是不成立的。


最后,B和BCPL模型在处理指针时,暗中会做得更多:语言规则,通过定义一个指针作为字数组的索引,强迫指针被表示为字索引。每个指针引用生成一个运行时,从指针到硬件要求的字节地址的度量转换。


因为这些理由,看起来需要一个类型模式来处理字符和字节寻址,以及为即将到来的浮点硬件作准备。其它问题,特别是类型安全性和接口检查,看起来并没有变得像以后那样重要。


除了语言本身的问题,B编译器的threaded代码技术得到的程序,比他们对应的汇编语言版本慢很多,以至我们对用B纪录操作系统或它的中心工具的可能性打折扣。


到1971年时,我开始通过添加一个字符类型,并重写它的编译器以生成PDP-11机器指令而非threaded代码,来扩展B语言。因此从B到C的转换,与创造一个同汇编语言竞争,能产生足够快和小的程序的编译器,是同时进行的。我称这个轻微扩展的语言为NB,表示“新B”(new B)。


C萌芽


NB只存在了很短时间,以至没有编写一个它的完整描述。它提供类型int和char,它们的数组,指向它们的指针,用典型风格声明如下


int i, j;
char c, d;
int iarray[10];
int ipoint[];
char carray[10];
char cpoint[];
数组的语义与在B和BCPL中保持一样:iarray和carray的声明产生的单元,被动态初始化为分别指向十个整数和字符序列中的第一个的值。ipointer和cpointer的声明省略了尺寸,以表明没有存储被自动分配。在过程内部,语言对指针的解释与数组变量是一样的:一个指针声明产生一个单元与数组声明的区别仅在,程序员被期望给它赋值,而不是让编译器分配空间和初始化单元。


值存储在数组的单元中,指针是按字节计算的,对应存储区的机器地址。因此通过指针间接引用,不意味着比按比例缩放指针从字到字节的偏移,有更多运行时开销。另一方面,对应数组取下标的机器代码和指针算术依赖于数组和指针的类型:计算iarray[i]或ipointer+i表示按比例缩放加数i与所指向对象的尺寸。


这些语义表示一个来自B的容易转换,我在它们上面实验了几个月。当我尝试扩展类型符号,特别是添加结构化(纪录)类型时,问题变得明显。结构看起来,应该以一种直接的方式影射到机器的内存,但一个结构包含一个数组,没有合适的地方隐藏包含数组基地址的指针,也没有方便的方式安排被初始化的对象。例如,早期unix系统的目录条目,在C可以被描述为


struct {
int inumber;
char name[14];
};
我希望结构不能仅仅是体现抽象对象的特征,也要描述可能从目录读到的位集合。编译器能在哪里隐藏指向语义要求的name的指针呢?即使结构被想象的更抽象,指针的空间也能以某种方式隐藏,我如何处理在分配一个,可能是一个结构包含数组再包含结构到任意深度的复杂对象时,完全初始化这些指针的技

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

评论

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