设为首页 加入收藏

TOP

C语言的发展史(The Development of the C Language) (六)
2014-11-23 22:19:18 来源: 作者: 【 】 浏览:7
Tags:语言 发展史 The Development the Language
最早期的用户和开发者的面前;相反我不得不在语言发展的同时,不断地改变现有程序,并且容忍现有代码。(后来,ANSI X3J11委员会标准化C面临同样的问题。)


在1977年或之后的编译器,没有对整数和指针间的赋值,或使用错误类型的对象来引用结构成员产生抱怨。尽管K&R第一版中的语言定义,在处理它的类型规则上是相当(尽管不完整)一致的,那本书承认已存在的编译器不用坚持那些规则。而且,一些为了简化早期过渡设计的规则,给以后带来了混乱。例如,函数声明中的空方括号


int f(a) int a[]; { ... }
是一个活化石,一个NB残余的声明指针的方式;在这个特殊的情况下,a在C中被解释为一个指针。这种表示,部分是应为兼容性而存活下来,部分是它允许程序与于读者进行交流的理由,向f传递一个数组生成的指针,而不是到一个整型的引用的企图。不幸地是,它所起到的迷惑的作用和它带来的提醒一样多。


在K&RC中,为函数调用提供正确类型的参数,是程序员的责任,而现有的编译器不检查类型约定。原始语言没有在函数类型签名包括参数类型,是一个重大,和真正需要X3J11委员会做出大胆和最痛苦革新的缺陷。早期设计在我对技术问题的预防中被解释了,尤其是分离编译原文件交叉检查,和我在对从无类型语言改进到类型语言的含义的不完整理解。在上面提及的lint程序,尝试缓解这个问题:作为lint的功能之一,lint通过扫描一系列源文件,比较调用时的函数参数类型和在他们的中的定义,检查整个程序的一致性。


对于已察觉到的语言的复杂性,产生了一些意外的语法。间接引用运算符,在C中被写作*,在语法上是一个前缀运算符,正象在BCPL和B中一样。这在简单的表达式中很有效,但是在更复杂的情况,需要括号来指示分析。例如,为了区分对函数返回值的间接引用与通过指针调用函数,分别写作*fp()和(*pf)()。表达式使用的风格贯彻到声明,所以这些名字可以声明为


int *fp();
int (*pf)();
在更过分但仍现实的例子中,事情变得更糟糕:


int *(*pfp)();
是一个到返回一个整型指针的函数的指针。出现了两个结果。最重要的是,C有一个相当丰富的类型描述(比如说跟Pascal比较)集合。比如在C——Algol 68描述对象,语言中声明与表达式一样,很难理解,只是因为对象它们本身很复杂。第二个结果归因于语法细节。声明在C中,应该用一个从内到外的风格来阅读,可能难以领会[Anderson 80]。Sethi[Sethi 81]发现,如果间接引用运算符被当作一个后缀而不是前缀运算符,许多嵌套的声明和表达式会更简单,但这时改变已经太晚了。


不管它的困难,我认为C声明的方式是合理的,并且觉得它很合适;它是一个有用的一致性原则。


C的其它特征,它对数组的处理,在实际中更令人疑惑,尽管它也有实在的优点。尽管指针和数组间的关系并不通常,它还是能被学会的。而且,语言在描述重要概念方面,表现了相当强大的功能,例如,仅仅使用几个基本规则和惯例,向量长度在运行时可变。特别是,字符串处理被采用与其它数组一样的机制,附加上null字符终结一个串的惯例。把C的那个方法与两个同时期的语言Algol 68和Pascal[Jensen 74]比较,是有趣的。数组在Algol 68中有固定边界,或可变的:很多机制被要求,存在语言定义和编译器中,为了提供灵活数组(并非所有编译器都实现了它们)。原始Pascal只有固定尺寸数组和串,这已被证明是受限制的[Kernighan 81]。后来,它是部分固定,尽管最终的语言没有广泛运用。


C把串当作在管理上用标记终结的字符数组。除了用串字面值初始化的特殊规则,串的语义完全包含于更通常的控制数组的规则,结果是语言比一个把串当作独立数据类型的语言,更易于描述和翻译。它的方式产生了一些开销:某些串操作比在其它设计里需要更多开销,因为应用代码或库例程有时必须查找串尾,因为几乎没有可用的内置运算,也因为串的存储边界管理极大转向了用户。而且,C的串处理方法很有效。


另一方面,C对数组的处理,通常(不仅指串)不幸在优化和以后的扩展有隐含意义。指针在C程序里流行,不论是显式声明或由数组形成,意味优化器必须谨慎,并必须使用小心的数据流向技巧才能得到满意的结果。复杂的编译器能理解大多数指针可能改变,但一些重要对于分析仍然显得困难。例如,带有从数组继承而来的指针参数的函数,在向量机器上很难编译生成有效代码,因为确定一个参数指针,没有重迭另一个参数指向的数据几乎不可能,或者外部可访问。更重要的是,C的定义如此明确描述了数组的语义,以致改变或扩展数组为更基本对象,以及允许把它们当作整体操作,变得不能适用当前语言。甚至允许声明和使用动态决定尺寸的多维数组的扩展,也不是完全直接[MacDonald 89] [Ritchie 90],尽管它们使用C中编写数值库更容易。因此,C通过一个统一和简单的机制,包含了实践中串和数组的最重要使用,但是把问题留给了高效率实现和扩展。


除了上面讨论的,语言和其描述中存在许多更小的不适当地方。也有常见的批评可以提及,但不是逐条表明。其中首要的是语言和它的一般要求的环境,没有为编写很大的系统提供帮助。命名结构只提供了两个主要的级别:“external”(到处可见)和“internal”(在单个过程内)。可见性的一个中间级别(在单个数据和过程文件内)与语言的关系很微弱。所以,没有对模块化的直接支持,项目设计师不得不使用自己的约定。


类似,C本身两种存储期:“automatic”对象当控制流程存在或低于过程时存在,“static”存在于程序的全部执行期。动态分配仅由一个库例程提供,并且管理它们的负担落在程序员一方。C反对自动垃圾回收。


因此成功?


C的成功远超出了早期的期望。哪些品质促进它得到广泛使用呢?


Unix本身的成功无疑是最重要的因素;它让这个语言可以被几十万人使用。相反,C在Unix中的使用和随之发生的到各种各样机器的可移植性,自然对系统的成功非常重要。但是,语言进入其它环境,提供了更重要的价值。


尽管某些方面对初学者甚至偶尔对老手都是神秘的,C不失为一个简单和小的语言,可被简单和小的编译器翻译。它的类型和操作充分依据于真实机器,让人们容易理解机器如何工作,并学会生成时间和空间效率程序的惯用法也不困难。同时,语言充分抽象于机器细节,程序可移植性也可以达到。


同样重要的是,C和它的主要库支持,总是保证能存在一个真实环境中。它不是被设计用来孤立验证某一点,或作为一个例子,而是作为一个用来写有用程序的工具;它总是意味着同一个大型操作系统交互,并用来创建更大工具。一个节俭、实际的方法影响了进入C中的事物:它覆盖了多数程序员的基本需要,但不尝试提供太多东西。


最后,不管自它第一次不正式、不完整描述发布以来经受的变化,真实的C语言,像几百万使用许多不同编译器的用户见到的一样,与那些类似流行的语言,比如Pascal和Fortran比较,保持着显著的稳定和一致性。存在许多不同的C的方言——最显著的是那些由更古老K&R和更新的标准C的描述——但基本上,C比其它语言保持着更自由的属性扩展。或许最重要的扩展,是用于处理某些Intel处理器的怪异之处的“far”和“near”指针

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

评论

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