第3章语言设计原理

第3章语言设计原理
第3章语言设计原理

3语言设计原理

3.1历史和设计标准

3.2效率

3.3正则性

3.4进一步的语言设计原理

3.5C++:语言设计实例研究

语言设计是计算机科学领域中最难理解和未知的领域之一。第1章曾强调过现代程序设计语言的主要要求是可读性、抽象和和复杂性的控制机制机制。但由于语言机制之间的相互作用很复杂,一般很难用这些标准来判断语言是否的成功与失败。

与语言定义没有直接关系的实际情况,也对语言是否的成功也和失败有着重要的影响。,如可用性、价格和语言翻译译码器的性能等因素,甚至政治、地理、时间和市场也具有一定的影响作用。C语言的成功,至少部分是由于Unix操作系统的成功,后者从而促进了C语言的使用。虽然COBOL语言被计算机科学界所忽略,但由于她在工业领域的使用及大量的传统应用,因而仍是重要的语言。而美国国防部工程项目的应用需求使Ada语言立即得到了推广取得了即时的效应,。另外Internet的发展使Java语言迅速获得了广泛应用。

语言的成功和失败同样存在各种各样的原因。语言设计者已经注意到:当个人或一个小组设计一个语言时,重要的是取得一致的认识和统一的设计理概念。事实上,Pascal、,C、,C++、,APL、,SNOBOL和LISP就是一些例子。另外同时,由专业委员会设计的COBOL 语言,多几个Algol语言版本和Ada语言也获得了成功。

第2章曾提到可试图用程序设计语言——用规范规约语言以及非常和更高级的结构代替程序设计以来实现更高级的抽象。这虽未取得成功,但仍有必要在比理论学家所希望的更详细的层次上说明算法和计算,为什么要这么做呢?部分原因答案是由于几乎每个程序设计都有不同的应用,并且需要不同的抽象。因此,必须为特定情况的语言设计提供合适的抽象方法,或根据要求为抽象方法的建立提供通用的设施。程序设计语言的设计主要依赖于预期的应用以及对应用的需求。在这一点上,语言设计类似于程序设计本身,它是即有目标的活动。

由于在语言设计中要对确定具体应用进行的抽象,因而对于专用语言,如数据库语言、图形语言和实时语言,牢记设计的目标是很重要的。对于通用语言也如此,只不过设计目标没有那么明显。在多数成功语言的设计过程中,要时刻牢记具体的设计目标。FORTRAN 侧重于执行效率;COBOL侧重于非技术性的类英文的可读性;Algol60是描述算法的分程序结构语言;Pascal是促进自顶向下设计的简单指令性介绍性语言。C++在保证效率以及和与C语言兼容的同时,还在更高的抽象方向满足了用户的需求。

即使记住了上述所有这些建议,要说明什么是良好的程序设计语言设计仍是很困难的。作为一个合适的例子,两位程序设计语言的先驱Niklaus Wirth和C.A.R Hoare对语言的设计曾提出了两种不同的建议。Pascal的设计者Wirth,认为语言的简洁性是首要的(Wirth[1974]);而著名的计算机科学家和许多语言的共协同发明者C.A.R Hoare,则强调单个语言结构的设计(Hoare[1973])。C++的设计者Bjarne Stroustrup则,指出语言并不单单是一些简单特性的集合(Stroustrup[1994],第7页);计算机科学的先驱Fred Brooks 认为,语言设计类似于其它设计问题,如建筑设计等方面的问题(Brooks[1996])。“设计哲学”或“可表达性”等似是而非的性质也常被用来描述语言设计问题。在下面几节中,将汇收集了一些一般的语言设计标准及一系列对语言设计者有帮助的具体原则。关于设计目标,还将给出强调性地给出所做选择可能好或者、也可能坏的一些特殊例子,以及对此这些问题的发生分歧看法的理解。(有时需阅读本书后面几节内容才能完全理解其中的一些实例)。

3.1语言的历史和设计标准

程序设计语言最早的一个主要设计标准是执行效率。(当时机器速度很慢,并且普遍认

为语言翻译器不能生成有效的可执行代码,因此程序的执行速度便很重要;并且普遍认为语言翻译器不能生成有效的可执行代码)。例如:可执行代码的紧凑和速度是FORTRAN的最主要目标。其实,FORTRAN代码被设计成尽可能与生成的机器代码相似。当然,应该牢记高级语言存在的全部理由是在编写程序上她比机器语言或汇编语言更容易使用于编写程序。可编写性是使程序员能清楚、正确、精确、快速地表达计算的一种语言的性能,使用具有较好可编写性的语言这有助于提高效率。很难获得到对人和机器都是可读的程序,因为当时程序要求短小,由一个人或几个人编写,且很少由不参加编写该程序的人进行修改或更新。

COBOL和Algol60向通用标准而不是向生成有效代码前进了一步。Algol60中的块结构和递归结构方法,在逻辑上为表示算法提供了更清晰、准确的方法,由此从而大大提高了语言的可编写性。(第1章中介绍过,C.A.R.Hoare在学习了Algol60之后,才对如何表示表达快速排序算法QUICKSORT有更深的理解)。Algol60被设计成不仅是人与机器间通信的语言,也是人们之间交流算法的语言。因此,使程序员更容易和精确地理解和掌握计算性质的程序可读性,也成了语言重要的设计目标。为使程序更接近普通的书面手写英文,COBOL 试图改善程序的可阅读性。在某种意义上,她并未取得成功,因为她不但没有改善读者在逻辑和行为方面理解程序的能力,反而因篇幅更长、更累赘,降低了程序的可编写性。但COBOL首次从而把程序可读性作为一个提为首要的设计目标。

60年代,随着语言复杂性的增加,语言设计者意识到更加需要抽象方法,需要减少程序员知道的规则和限制,这些都说明了必须对复杂性予以控制。一方面,抽象使程序员能控制程序设计任务的复杂性。另一方面,减少语言的规则和限制降低了语言定义的复杂性,并使语言更容易用于解决实际问题。Simula67的目标是提供更有力的抽象方法;Algol68试图使用完全一般的和正交的方法去降低语言的复杂性:使性质设计的限制尽可能地的少,并用各种合理的办法把这些性质合并,见3.3节。Simula67引入的类的概念是一种创新,对70和80年代许多语言提出的抽象机制都有影响。但Algol68的一般性和正交性并未取得成功。Wirth[1974]指出,虽然一般性减少了具体规则的数目,但事实上却增加了复杂性,因为太一般的构造更难以理解,其效果更难以预测,且更难在概念上定义底层的计算模型。Stroustrup[1994][1996]也指出,Algol68和Simula67中描述的追求概念一致和优美导致了实现的无效性,且很难适用于某些上下文。

70年代到80年代早期,正如Pascal,C,Euclid,CLU,Modula-2和Ada等语言中所体现的,更加强调语言的简单性和抽象性。同时还尝试对一些构造引入数学定义以改善程序的可读性,给为语言提供一些方法让使翻译器在执行翻译的过程中可以证明程序的正确性。但是,程序证明系统只取得了有限的成功,其主要原因是由在于,不但对程序设计和语言设计,而且对翻译器本身,在引入证明机制后都增加了复杂性。强类型是这些努力的一个成果,目前已并成了多数语言的标准组成成份,其实在不同程度上,也是上面提到的5种语言中的一部分。

80和90年代,人们仍然对提高语言的逻辑的和数学的精确性感兴趣,而且还尝试使逻辑本身成为一种程序设计语言。ML和Haskell的发展及Lisp/Scheme的广泛使用,又重新激起了大家对函数式语言的兴趣。

近15年来最重要的发展是面向对象语言Smalltalk、C++和Java所取得的实际成效,以及从这些语言关于面向对象语言关于语言抽象方法的实践经历方面的检验。业已证明,设计目标中最成功的是抽象机制,因为这种机制适应了现实世界对程序设计任务的需求,。还有的就是解决对语言起到扩展问题的作用的解决具体问题领域的库机制的使用,以及为提高代码灵活性和复用性的面向对象技术的应用也获得了巨大成功。

这些年来,由于不断吸取先前语言设计的经验,以及计算机科学面临问题的性质的有所改变,使得不同语言设计目标的重点也发生了变化。然而,可读性、抽象和复杂性控制仍是大多数语言设计所要考虑的问题。

3.2语言的有效性

本节将列举上节提到的一些更具体的原则,且针对每个原则给出设计上正反两方面的实例。这里先从应用于不同形式的普遍需要的有效性开始。

该原则以不同的形式几乎包括了所有的其它原则。通常最先考虑的是目标代码的有效性:语言的设计应该使编译器能够生成有效的可执行代码,常称之为最优化。例如,静态地确定类型变量能有效地生成分配和引用这些变量的代码。类似的地,C++的类方法设计也是如此,但在没有更高级的面向对象特性的C中,较简单的struct机制却不需要额外的内存或代码。

其次是翻译的有效性:语言的设计能否使用速度快和、长度适中的翻译器有效地翻译语言的源代码?例如,设计的语言能否使编译器按一次扫描来编写?Pascal和C正是这样的,变量必须在使用之前予以说明。然而,C++取消了这种限制,为解决变量的引用,编译器必须对代码进行两次扫描。有时,很难在翻译或执行时检查语言设计中的某些规则。例如,Algol68禁止对悬垂挂引用进行赋值。偶尔,语言设计者会有意让翻译器不检查这些规则来避开这种非有效性。其实,出错检查通常会影响有效性,因为在翻译时检查错误会降低翻译器的效率,同时在执行时生成检查出错的代码,也会降低目标代码的效率。但是,忽略出错检查会破坏另一设计原则,——可靠性,该原则能保证在执行过程中不会陷入无法预测的或灾难性的状态。

除了上述两种观点之外,还有许多关于有效性的理解。一种是可实现性,或是编写翻译器的有效性。它不仅和翻译的有效性有关,而且还是语言复杂性定义的一个功能。由于语言的翻译器难以编写或不能充分理解执行翻译的算法不能被充分理解,都关乎到将导致语言的失成败。Algol60在美国用的较少的一个原因可能是,运行系统所需的栈结构在当时没有得到广泛的接受。程序设计语言ML中无说明的类型推理,必须等待应用合一算法进行类型推理。Ada中开发编译器的一个障碍是规模和复杂性问题,这且消弱了其可用性及用途。有时,一种语言的设计要求用已知的方法无法满足,这使得该语言很难翻译。Wirth[1974]特别强调可实现性原则:“语言的设计就是对构造编译器”。

有效性的另一种观点是程序设计的有效性:用语言编写程序的速度和难易程度如何?这主要指的是前面介绍过的可编写性。程序设计有效性的一个指标是语言的表达能力:是否容易表示复杂过程及其结构是否容易表示?或者是否容易将程序员的思想映射为实际的代码?显然这与语言抽象方法的能力和一般性有关。语法精确化和避免变量说明等不必要的细节也是这种有效性的重要因素。若从这个观点理解,Lisp和Prolog应该是理想的语言,因为它们的句法是极其精确的,不需要变量说明,并且把许多计算细节留给运行系统。当然,这与其它的语言设计原则,如可读性、执行的有效性和可靠性之间要做出妥协和折衷。

其实,可靠性本身也是一种有效性。对于不可靠性的程序要付出更大代价,如隔离或清除错误状态所作的修改,额外的检测时间,及纠正错误状态产生的影响所需的时间等。倘若程序极不可靠,那么将会浪费掉整个大量的开发时间和编码时间。这种有效性在软件工程中是一种资源的开销。在开发软件的意义上,有效性依赖于可读性和可维护性(找错误、修改及增加新性能的容易程度),而可编写性却要退居次位。根据软件工程师的估计,花费在调试和维护上的时间要比花费在编写最初程序代码上的时间多的多。因此,可读性和可维护性最终还应归结为最重要的有效性问题。

3.3正则性

正则性是语言中难以定义的特性,体现在如何将语言的一些特性溶为一体的良好程度,好的正则性要求特定特殊结构中不常见的限制较用的较少,结构之间奇特的相互作用较少,语言特性中奇异的运行方式较少。正则性通常分为三种更确切的概念:一般性,正交性和统一性。一个语言要获得一般性,应该避免可用性和结构使用中的特殊情况,及避免把紧

密相关的结构组合成单个的更通用的整体。正交性是源于数学中的一个术语,意思是处于垂直或完全无关的方向。程序设计语言中的正交性表示语言的结构可以用任一有意义的方式组合,同时结构的相互作用和使用的内容不应该产生未预料到的限制或行为。统一性的意思是,类似的东西应该看起来相似且具有类似的意义,反之亦然,,不同的东西应该看起来完全不同。

下面给出关于这三条原理的若干实例(主要是不符合这些原理的结构)。这些例子将清楚地说明,三个原理之间的区别通常是属于观点上的不同而非实质性的不同,同时当三个原理在某个子范畴不完全满足其特性时,可将该特性或结构简单地归为非正则性。

一般性这里列举几个说明通用程序设计语言缺乏一般性的的性质。

·Pascal有嵌套函数和过程,它们可以作为参数传递给其它过程,但没有过程变量,因此Pascal的过程表示缺乏一般性。C不含有嵌套过程和函数定义,因此过程也缺乏一般性。然而,C允许参数、变量和函数返回值为过程(被C语言社团称为由C共用体调用的函数指针)。相比较而言,Scheme和ML等多数函数式语言具有完全通用的过程和函数结构。·Pascal不含变长数组,因此数组缺乏一般性。而C和Ada却有变长数组,FORTRAN能够传递变长数组,但不能定义变长数组类型。

·C中相等操作符“==”不能直接用于比较两个结构体和数组,但可用于比较元素。因而,相等操作符缺乏一般性。Ada已取消了这种限制,C++中部分地取消了该限制。更常见的是,许多语言不具有扩展新数据类型的预定义操作符的方法(如==或+)。而一些语言如Haskell则允许由用户创建新的操作符,但Ada和C++不具有这种机制。在这些语言中,操作符已经实现了完全一般性。

·FORTRAN中不存在命名常量。Pascal中的常量不能是表达式,而Modula-2的常量表达式不能含有函数调用。然而,Ada却具有完全通用的常量说明机制(即使常量是动态量)。正交性该观点是,不同的上下文中语言的结构不应该代表不同的性能。因而,上下文相关的限制是非正交的,而不考虑应用于上下文的限制是非一般性的。其实,前面介绍的Modula-2中常量的非一般性可以解释为表达式的非正交性:在常量说明中,表达式只可以是有限制的形式。类似的,相等比较的非一般性也视为一种非正交性,因为相等比较的可应用性与正在比较的值的类型有关。这里列举一些缺乏正交性的实例:

·Pascal中,函数只能把标量和指针类型作为返回值。C和C++中,函数可以返回除数组类型外的其它数据类型的值(其实,在C和C++中,数组的处理与其它所有类型都不同)。Ada和多数命令式语言均取消了这种正交性。

·在C中,局部变量只能在分程序的开头定义(复合说明)1,而在C++中,可以在分程序内的任何地方定义变量(当然必须在使用之前定义)。

1新的ISO C标准(ISO9899[1999])取消了该限制。

·在C语言中,参数的传递存在非正交性:数组按索引传递,其它参数按值传递。

正交性是Algol68的主要设计目标,也是语言设计的最好实例,其中的构造可以用各种有意义的方式进行组合。

统一性该原则主要集中于语言结构形式和性能的一致性。非统一性包含两类:相似的东西看起来不相似,或者性能上不相似;不相似的东西实际上看起来相似,或者性能上不该相似的却相似。非统一性的实例有:

·C++中,类的定义之后必须有分号,而函数的定义之后却不要分号。

class A{……};//需要分号

int f(){……}//不需要分号

·Pascal的函数返回值看起来同赋值语句很相似:

function f:bollean;

begin

f:=true;

end;

多数语言使用返回语句表示该操作。还有一种情况是,不同的东西应该看起来不同,但却迷惑性的使人以为其相似。C的操作符“&”和“&&”就具有相似的情况:第一个操作符表示“按位与”,第二个操作符表示“逻辑与”。这两个操作符看起来相似,却产生不同的结果。

在某些情况下,非统一性可视为非正交性,因为具体上下文中的非统一性可以看作结构间的相互作用。

为什么语言能够具有非正则性?当然,语言设计者并不是有意建立这些不平常的限制,相互间的作用和混淆性。由于含糊不清及某些历史上的原因,非正则性成了语言设计研究的真正难题,但它们却往往具有很大实用价值。

例如,上面指出的C++中的分号问题属于非统一性问题。由于C++试图尽可能少的偏离C语言,因此必须和C相兼容,不得不实行非正则性。

C和Pascal中的函数也必须实行非正则性(尽管可以对不同的限制采取不同的选择方法),因为这两种语言具有简单的基于栈的运行环境,见第8章。如果函数没有某些限制,那么将需要更通用的环境,这与可实现性的简单和效率相互折中。

最后应该指出的一点是,确立目标的希望太大,如语言设计的一般性或正交性是不可取的。Algol68就很好的说明了这一点,虽然该语言在满足一般性或正交性目标方面,获得了很大的成功,但是许多语言设计者认为该目标自身就导致了语言的含糊不清和复杂化。

如果不加限制的使用某些性质,也会严重影响可读性和可靠性。例如,Pascal通过禁止使用指针减少匿名和不安全性,而C为取得更好的通用性,则允许使用指针,但这容易产生误用和错误。Java语言取消了指针的使用(蕴涵在所有对象的分配中),但却增加了一些开销:运行环境更复杂,以更难理解的方式改变变量和参数。

在评价非正则性是否合理时,必须把语言的基本设计目标同消除非正则性时可能产生的复杂性联系起来。如果不能以该方式证明非正则性,那么这将会是设计中的缺陷。

3.4进一步的语言设计原理

从前面几节中已经知道,有效性和正则性是语言设计的原理。在3.2节中也已简单介绍了其它一些原理,如:

简单性前面曾几次提到简单性原理。经历了Algol68和PL/I的复杂化以后,简单性成了Pascal的主要设计目标。简单性也是C语言的一个特点,虽然它只是C语言产生有效目标代码(适于编写Unix操作系统代码)和小型编译器(适合小型计算机)的间接设计目标。简单性也是Pascal成功的一个主要原因。表面上很容易获得简单性,其实在实践中是相当困难的。首先,正则性不是简单性。Algol68是正则性最好的语言之一,但其缺乏简单性。简单性并不是指包含很少的基本结构,虽然这些结构很有用:LISP和Prolog只有几个基本的结构,但却需要复杂的运行系统。另一方面,过于简单的程序设计语言实际上会使编程任务更加复杂。BASIC虽是一种简单语言,但由于缺少某些基本的结构,如说明和分程序,因而很难用该语言编写大型应用项目。Pascal本身也存在过于简单的问题:缺少良好的串处理、独立编译、合理的输入输出机制,且有许多非统一性问题,如把赋值用作函数的返回。其实,Pascal的过于简单正是C,C++和Java替代该语言的主要原因。C在简单性方面可以说是进行了较成功的尝试,尽管也存在许多缺陷:不完善的串处理机制(类似于Pascal),模糊类型和操作符语法,不常用的数组语意及弱类型检查。

这里也许值得回忆爱因斯坦的一句名言:每件事情都应该尽量简单,但不能过于简单…。

过于简单反而使语言用起来更麻烦,且缺少表达能力、可读性和安全性,同时还存在更多的限制。

表达能力前面曾提到,表达能力是促进程序设计有效性的一种方法:表达能力是指用语

言表达复杂过程和结构的难易程度。最初对表达能力的改进是在程序设计语言中增加了递归方法(LISP和Algol60)。LISP是具有表达性的语言,因为在程序运行时可以通过任意的方式改变数据和程序。在数据的规模和形式未知的复杂情况下,该表达能力尤其有用。但是众所周知,表达能力同简单性是相矛盾的:LISP,Prolog和Algol68都是极具表达能力的语言,但它们并不具备简单特性,部分是由于它们良好的表达能力。

表达能力也是面向对象语言流行的一个原因:因为面向对象特性极大的提高了程序员根据设计编码的能力。Stroustrup[1996]曾提到,正是由于Simula67给他留下的深刻印象,才促使他去设计C++:“Simula67语言的概念给我的印象特别深刻,这对我考虑应用中的许多问题起了帮助作用”。2他还指出,该表达能力也可以直接产生可读性:“类概念可以使我通过直接的方式将应用概念映射到语言结构中,这比用我所见到的其它语言编写的代码具有更大的可读性”。3

2Ibid.,page700

3Ibid.,page700

表达能力也常视为简要性,这实际上可折衷地解释为可读性。在这种意义上,C是具有表达能力的语言,可是许多程序员并未发现C中如:4

while(*s++=*t++);

等容易理解的表达式。

4拷贝字符串的著名例子,见Kernighan和Ritchie[1988],105页

可扩充性该原则认为应该提供用户一些通用的机制,让用户为语言添加新的特性。随着观点的变化而需要“添加新特性”,简单的说就是能够定义新的数据类型,许多语言允许这一点。从更深层次的角度理解,是指能从库中增加新的函数,许多语言也是允许这一点的。可扩充性还可理解为,向翻译器本身添加关键字和结构。在LISP等函数式语言中,不难实现可扩充性。尤其在LISP中,语言的结构可以根据语言自身定义。因此,LISP是可扩充语言,她只提供了少量的内部结构或基本成份,且可根据环境需要增加其它的操作。而在命令式语言中,实现可扩充性是比较困难的。语言当前倾向于不全面建立可扩充性:允许用户定义新的数据类型及相应操作,且首先在这种语言中允许出现已定义过的操作。

例如,可以通过下面的方式为Ada添加矩阵数据类型,使得矩阵操作同普通的整型或实型操作一样。首先给出说明:

type MATRIX is

array(POSITIVE range<>,POSITIVE range<>)of FLOAT;

function“+”(LEFT,RIGHT:MATRIX)return MATRIX;

A,B,C:MATRIX(1..10,1..10);

由此,可以编写:

C:=A+B;

这里,“+”操作符被重载。C++中通过类的使用给出了类似定义,更详细的内容见第5章和第10章。

C++和Ada中的“+”等操作符的重载只局限于已存在的操作符,并且得必须理解这些操作符相同的语法特性(如左结合性和优先权,见第4章)。在极少数语言中,如ML和Haskell 等函数式语言,可以增加用户定义的操作符。例如:程序中对“+++”的定义,用Haskell 语言描述如下:

Infixr6+++

a+++b=…

“+++”是中缀操作符,位于两个操作数之间,具有右结合性(“右”(r)结合到“Infix”)和6级优先级(该特定语言中与“+”优先级相同)。

在过去的十几年,随着应用范围和相互作用的增长,可扩充性已经成为语言属性的一个重要因素。就扩展库的能力及语言和系统之间的相互作用而言,不具有可扩充性的简单

语言实际上已不能存在于当代的计算世界中。

可扩充性使语言设计者有更多选择性,包括建立核心语言和标准库方面的可用特性,及根本就不指定哪个方面的可用特性(但允许提供用户(third-party)库方面的特性),且不必过多担心错误的选择。例如,Ada语言内核中具有并发的程序设计特性(任务机制);Java标准库也包括这些特性(线程库);C++却没有规定任何具体的并发特性。

可限制性语言的设计应该能让程序员使用语言的最少知识和最少的语言结构进行编程。因此,语言的设计应该提高定义语言子集的能力。这在两个方面会有好处:第一,程序员不必为了有效的使用该语言而掌握全部内容;第二,如果实现整个语言花费太高或者没有必要时,编译程序的编写者可以选择性的只实现某个子集。随着C++和Java等现代语言的规模和复杂性的增加,限制性已经和可扩充性同等重要。当然,有人会从简单性的角度问,若仅使用语言的一个子集就能有效的编程,为何不首先让该子集成为整个语言,把别的内容作为库?对于通用语言,不同的应用会需要不同的功能,因此很难决定语言应该包括哪些功能而不应该包括哪些功能。例如,并发和异常处理机制仅适合于某些应用。同时相比较而言,语言本身比用库处理某些特性更容易些。由于在某些情况下,每种表示具有更好的表示性,因此使用不同形式的同类结构的表示常常是有用的,如repeat语句和while语句。严格意义上,这种语法糖衣(sugar)是不必要的,但它有助于程序的理解。另一方面,若一件事有十几种方法,为降低表达能力的代价,必须增加语言的复杂性。

有效性是常被忽略的可限制性的一个方面:原则上讲,若一个程序没有使用语言的某些特性,那么这些未被使用的特性将不会降低该程序的性能。换而言之,未使用异常处理机制的C++程序,并不比用没有异常处理机制的C编写的等价程序速度慢,简单原因是C++有异常处理机制却可以不用。其实,这也是C++的一个主要设计目标。

记法同习惯一致对于有经验的程序员来说,程序设计语言应该是易学易理解。为此,语言的设计应尽可能多的合并那些标准的性能和概念。程序、函数和变量等标准概念都应能清楚识别。Algol68在许多方面破坏了这个原则,如使用保留字mode而不使用type。Algol60的自由格式已成为标准,而FORTRAN却抵制该机制的使用,后来空白习惯的用法成了设计的一个原则,允许空白的行。分隔符和标识符的保留字使程序更清楚且更易读。由于FORTRAN没有使用空白,这会在可读性和安全性方面出现问题,如下面的例子所示:DO99I=1.10

去掉空格后,该语句变成了把值1.1赋给变量DO99I的赋值语句。这种奇怪现象很适合称为违背了“最料不到的惊奇规则(law of least astonishment)”:事情不应该以完全预料不到的方式起作用或出现。

精确性有时也称为确定性,即对语言存在性的精确定义,从而使得可以预测程序的行为。精确的语言定义不仅有助于程序的可靠性,且增加了翻译器的可靠性:精确定义的语言会有更佳的可预测的翻译器,从而,程序的行为不会由于从一台机器移植到另一台机器而有很大的变化。获得精确性的一种方法是由设计者出版语言手册或语言报告。另一种方法是采用国家或国际标准组织的标准,如美国国家标准局(ANSI)和国际标准组织(ISO)的标准。LISP,FORTRAN,Ada,Pascal,COBOL,C和C++等许多语言都发布了各自的标准,这正是这些语言具有可用性的主要原因。要使语言标准或参考手册可用,不仅要使其尽可能地精确,而且对多数用户都应该是完整的。Algol68的设计者为了能获得更大的精确性,发明了许多用于描述语言的新术语。结果使得参考手册很难阅读,并且使得该语言未能得到他人的接受。

机器无关性这一在语言定义中应遵循的原理指语言实现应与特殊机器无关。实现机器无关性的重要方法是使用一些预定义数据类型,使得在语言中不涉及内存分配细节和机器体系结构。不幸的是,这些数据类型不能完全摆脱机器问题。例如:实型数据要求数据无限精确到特定值,然而计算机只能实现有限精度的数据,这种关于精度的问题很难采用完全与机器无关的方式解决。因此,语言设计应尽可能分离并确定那些与机器相关而无法避免

的事情,让程序员明白难题可能出现在哪里。在C标准库limit.h和float.h中,实现定义常量的有效方法就是解决机器无关性的很好实例。Ada语言更进了一步,它包含许多在程序中具体说明数字精度的设施。从而消除了对特定机器运算精度的依赖性。

安全性这一原理从两个方面促进语言设计:1、减少编程错误;2、允许发现和报告错误。安全性与可靠性及准确性密切相关。正是这一原则引导语言设计者把类型、类型检查和变量声明引入到编程语言中。这就是所谓的“不能让程序员把错误搞得最大”的思想(hoare[1981])。这种思路在语言的表达能力和简洁性方面采取一种折衷方案。并且通常是程序员承担大量繁重工作,在实际代码中尽可能多的确定若干项目。关于把提高安全性的特点引入到语言设计中的合理性,迄今仍有争议。LISP和Prolog程序员经常会觉得当试图编写复杂的操作,或设计能被各种数据变量使用的“通用的”实用程序时,静态类型检查和变量声明就成了主要的计算。另一方面,在工业、商业和国防应用中通常要求更高的安全性。也许实际的问题是如何设计一种既安全又有最高表达能力的通用语言。在这个方向取得进展的例子是ML和Haskell语言。它们采用函数式处理方式,允许多种对象,不需要声明,但仍需要执行静态类型检查。

3.5C++:语言设计的实例研究

绝大部分程序设计语言的历史和设计没有留下详细的文档,除了两本《程序设计语言的历史》(Wexelbatt[1981]和Bergin,Bibson[1996]),很少有关于程序设计语言如何发展的单独出版物。这可能由于以下几个原因:1、计算机技术高速发展,没有时间编写文档。(这种软件业普遍存在的问题,在语言设计中也不例外);2、部分语言研发者对记录历史没有兴趣;3、部分程序开发者普遍存在抵触情绪,不愿意揭露幕后秘密,包括一些错误和挫折。(这是各种科学家中常见的一种态度)。

在这些缺乏历史性信息的语言中,C++是个例外。她的起源、设计和发展都被其研发者Bjarne Stroustrup(Stroustrup[1994]和Stroustrup[1996])详细记录下来。确实,Stroustrup在历史和哲学方面的兴趣,就像对设计过程一样格外开明,独立给出了一种使语言设计取得成功的主要方案。C++设计中的某些重要问题,将在下一节概述;参照Stroustrup中关于一些细节的解释,这些解释对那些对语言设计很感兴趣的人颇值一读。·背景

Stroustrup关于设计一种新型编程语言的兴趣,诞生于20世纪70年代他在英国剑桥大学做研究生时的经历。他的研究集中于分布式系统上运行的软件模拟器。这些第一次写入Simula67的研究成果中的抽象机制(主要是类结构),正是用来表达他的模拟器的概念组织的设想。另外,他发现Simula的强类型检查在程序设计中,对于纠正概念性错误非常有用,他还发现,当程序越大时,这些特点就变得越有用。

不幸的是,他同时还发现大程序在有效的时间内很难编译和连接,慢得几乎无法运行。编译和连接的问题很恼人,而运行时间的问题更是灾难性的,因为这使他无法得到完成博士论文所必需的运行期数据。经过一番研究,他得出结论:运行期的效率低是Simula67的内在弊病,为了完成学业,不得不放弃Simula语言。他开始使用BCPL(一种低版本的C 语言的前身)重写他的整个程序。这种语言完全不适合正确表达他的程序中的抽象概念。这些努力经历曾经是如此痛苦,使Stroustrup感到他再也不应该试图采用现有的语言来完成自己的学业。

但是在1979年,作为新泽西贝尔实验室的一名新成员,Stroustrup再一次面对一个类似的任务:模拟分布于局域网的Unix内核。他很快决定需要一种新的编程语言。C语言使他了解了贝尔实验室(C和Unix的故里),他决定以C为基础,并加入他觉得很有用的Simula67类结构,来形成这种语言。

Stroustrup选择C的理由是由于其灵活性、高效性、可用性和轻便性(Stroustrup[1994],第43页)。这些特点也很适合新语言的设计目标(同上,第23页)。

1、用类、继承、强类型检验的形式支持优秀的程序开发。

2、按C或BCPL的顺序高效执行。

3、良好的轻便性,易于实现,易于与其他工具链接。

·第一次实现

Stroustrup的新语言是在1979~1980年最初实现的。该语言能用预处理器Preprocessor Cpre的形式把代码转化为原始的C。这种语言自称为带有类的C。他加入了满足上述三个设计目标的特性,Stroustrup称之为“medium success”(同上,第63页)。但Cpre中并不包括几个很重要的特点,如方法的动态绑定(“虚函数”,参见第10章)、类型参数(“模板”参见第6章和第10章),或常用重载(参见第5章)。Stroustrup决定把这种语言向这些方向及其它方向扩展,并用一个更复杂的真实编译器来替代预处理器(为保其轻便性,仍以生成C代码作为实现目标),这种语言诞生于1985年,现称为“C++”,其编译器称为“Cfront”(至今仍是很多编译器的基础)。他还对语言发展的基本目标进行了扩展,详述如下:

1、从实践来看,应尽量维持与C的兼容性。(“不应该无故不兼容,”同上101页)。

2、这种语言应该在实践经验的基础上不断的发展。就是说:C++语言的发展应由实际

的编程问题来驱动,而不是出于理论上的争论,同时应当避免特性主义“featurism”

(不要仅因其有可能就增加一个“纯粹”的特点)。

3、任何新增特性的实现,必须以不降低运行时间效率为前提。如果不行,就要求在不

使用新特性时程序不会增加额外开销(即“零开支”规则,同上,第121页)。

4、这种语言不应强迫程序员使用单一程序设计形式;就是说C++语言应该是一种“多

种花样”的语言。

5、这种语言应维持和强化其强类型检查特点(与C截然对立)。(“并不意味着有违静

态类型系统”,同上,第201页)。

6、这种语言应该可以分阶段学习,就是说尽可能的在使用C++的某些特性编程时,

不需要了解语言中其它不用的特性。

7、这种语言应保持与其它系统和语言的兼容性。

这种语言现在包括静态绑定、函数、操作符重载和改进的类型检查,但没有类型参数,异常,多重继承(这些都将在后面讲到)。

·发展

1985年下半年,Stroustrup出版了他的《关于C++》的第一版(Stroustrup[1997]是该书做了很大改动后的第三版)。其间,出于教育目的,Cpre和Cfront已经完全无偿的到处传播,在工业界,对于这种语言的兴趣已经迅速增长。因此,在1986年开始首次在商业中使用,1987年,第一次关于C++的专门会议由USENIX(the Unix user association——Unix用户组织)组织召开,到1988年10月,随着同年C++编译器的PC版问世,Stroustrup 估计共有15000的C++语言用户(从1979年10月Stroustrup自己一人使用Cpre到1991年,Stroustrup估计每7个半月C++用户就翻一倍)。这种语言的成功告诉Stroustrup和世人,关于标准语言定义的联合尝试是必要的,包括还未实现的可能的扩展。

·标准化

由于C++在使用中的快速发展和不断变化,存在着很多版本,因此其标准化工作决非易事。此外,由于用户群的增长,尽可能快的承担和完成这一工作还存在着一些压力,同时,Stroustrup认为,该语言的演化还没有完成,应该再给他一些时间。

然而,在1989年,Stroustrup还是出版了一个参考手册(注释参考手册,或ARM;Ellis 和Stroustrup[1990])。这本书包括所有C++的上述观点;包括新的异常处理,模板(类型参数),机制(多重继承已于同年加入到该语言和Cfront中)。1990和1991年,ANSI和ISQ标准委员会分别组成;很快他们结合彼此的工作,并把ARM作为标准化工作的“基本文挡”。

然后开始了一个长期的整理现有语言的过程;并增加对它的特点的精确描述,增加了

公认的新的特性和机制(主要是ARM外的次要特性)。1994年,有一个重要的发展,即加入了容器和算法标准库(标准模板库,STL)。标准委员会于1995年4月和1996年10月颁布了两套推荐标准草案,并于1997年11月推出了一套建议标准,这一标准于1998年8月成为实际的ANSI/ISQ标准。

·回顾

显然,C++语言是成功的——特别从用户的增长率和用户总数来看取得了震撼性的成功。为什么会如此成功呢?也许,最重要的原因,正是人们对面向对象技术不断增长的爆炸性兴趣,才使它把面向对象特点变成了计算的主流特点,因为它直接使用基于C的语法,不受限于特定的操作环境,且不牵扯性能方面的副作用。其它导致C++如此流行的因素包括它的灵活性、多态性和设计者结合实践经验对它的可扩展性。(Stroustrup在C和Simula67之后列举了Algo168,CLU,Ada和ML中的主要影响)。

C++的设计有无“错误”?许多人认为C++就像从前的Ada和PL/I语言一样,是一种开发套件“ketchen-sink”语言,因为它可以用多种特点和多种方法来做同一件事。Stroustrup (Stroustrup[1994],第199页)对C++的规模辩解如下:

C++规模大的基本原因在于它支持多种方法来写程序……这种灵活性自然使那些认为做事情只有一种正确做法的人不满……C++是倾向加入语言复杂性,是为了解决更复杂的程序设计任务。

然而,Stroustrup指出了一个“最大的错误”(同上,第200页),就是C++在第一次定义和实现时没有标准库(甚至没有strings)。虽然这一点在1998C++标准中已予改正,但是Java表明一种新语言的重要特点就是将其设计成一个可扩展的库,包括图形、用户界面、网络和并发。这恐怕是未来C++所面临的最大挑战:Stroustrup自己评价到“将来,我希望看到主要的活动和发展从语言转为……工具、环境、库、应用等。”

·练习:

3.1举例说明你所选语言的特点是促进还是违背下列设计原理:

效率表现力可维护性可读性

可靠性安全性简洁性可写性

3.2举例说明:正交、一般性、一致性三个概念的区别。

3.3在Ada中有loop……exit结构;PL/I中有类似的loop……break结构。在C和Java

中是否有类似的结构?这是某种设计规则的一个范例吗?

3.4Java中,整型量可赋给实型量,反之不成立,这违反了什么规则?在C中没有这种限

制,这又违反了什么规则?

3.5对于你选择的编程语言,找出一种你认为应该去除的特性,为什么应该去除这种特性?

去除后会产生什么后果?

3.6对于你选择的编程语言,找出一种你认为应添加的特性,并说明为什么应该加上?对于

这一特性怎样详细说明其行为,以及与其他特性间的相互作用。

3.7在Ada中保留字end必须放在以下的块后做结尾:if……then……end if,loop……end

loop等。在Algo168中,end被反写的保留字代替,如:if……then……fi,while……

do……od等。讨论这些约定对于可读性,可写性,安全性的影响。

3.8一种语言需要变量声明吗?某些语言如fortran和basic允许使用未声明的变量,而

C,Java和Ada要求所有的变量必须声明。从可读性,可写性,效率,安全性及可表达性上讨论变量应被声明的必要性。

3.9对照并比较在Hoare[1973],Hoare[1981]和Wirth[1974]中表达的观点。

3.10在C++中,分号用作不一致性的例子,讨论分号在C中的应用,是否完全一致?

3.11以下是关于程序中注释的两个相反的观点:

(a)程序应包含详细的注释,使之可读、可维护。

(b)程序本身应尽可能的文档化,只有在代码本身不清楚的地方才加多余的注释。

从语言设计的角度讨论两种观点。什么设计特性支持一种观点而不利于另一个?什么设计特性对两个观点都支持?

3.12下面有两个更加对立的观点:

(a)错误检测程序不应在错误检测关闭后再运行。测试完程序后,即行关闭错误检测

程序,就好像一经池塘里毕业,便撇开救生员进入海洋一样。

(b)在测试阶段以后,程序总应关闭错误检测程序。保持错误检测处于打开状态,就

好像进入自行车比赛后仍在车上带着训练轮一样。

从语言设计的角度来讨论a和b。

3.13讨论下面关于语言设计的两个观点。

(a)语言设计就是建造编译器。

(b)语言设计就是软件工程。

3.14Ada和C是编程语言在注释说明上两个对立观点的代表。对于Ada,注释由相邻的连

字符开始,并以行尾为结束:

--this is an ada comment

对于C,注释由“/*”开始,直到对应的“*/”结束:

/*this is a comment*/

比较两种注释方法的可读性、可写性和可靠性。C++加入了一种很像Ada5的注释规定(两个前斜杠),为什么C++不完全使用Ada的约定?

51999ISO C标准把对C++的注释规定加到了C中

3.15局部性原则要求变量声明应尽可能靠近程序中使用它的地方。什么语言设计特性支持

或违背了这一原则?Ada、C、C++、Java怎样支持这一原则?

3.16如果有好的符号运行调试器,一种语言会较为易学易用。比较你所知道的语言系统中,

调试器的使用和/或质量。你能想到哪些支持或不支持这种调试器使用的语言结构?

3.17编程语言设计中的一个最主要问题是缺乏对影响语言应用的诸多人为因素的理解,人

为因素包括从心理学到人机交互作用(人类工程学)。一个例子是:人为因素造成的编程错误往往不是随机的,程序设计应尽可能避免最常见的错误。纪录学习一种新语言中最常犯的错误,或查阅Ripley和Druseikis关于Pascal的研究。怎样才能避免在语言设计中最常犯的错误?

3.18现在很多编程语言使用Algo160倡导的自由格式:声明可以从任何地方开始,结束不

以正文中一行的结束为结束,而是以特定的结束符为结束,如分号。与之相反,FORTRAN 和其它一些语言使用固定格式:声明必须在特定的列开始,除了有连行符外均以行的结束为结束。讨论固定格式和自由格式关于可读性,可写性和安全性的作用。

3.19有一段话引自D.L.Parnas[1985]“因为生产力有了非常大的进步,才注意到了编译器

语言的引入,很多人希望通过引入更好的语言来取得更大的进展。好的表示法总会有所帮助,但不能期望新语言所提供的进步像第一次引入这种语言时一样巨大……可以想方设法简化编程语言,可是不能期望这会带来太大的改观。”试讨论之。

3.20一种可能的附加语言设计原则是可学性,就是说程序员快速、高效地学习怎样使用一

种语言的能力。试说明可学性是编程语言很重要的要求,并说明语言设计者提高可学性的方法。

3.21在大多数语言实现中,整型数据类型大小是固定的,就是说整型数的最大值依赖于机

器。在某些语言如Scheme中,整型数可以是任意大小,且与机器无关。讨论把“无限精确”整数作为语言定义要求的优点和缺点。能实现“无限精确”实数吗?实数可否实现机器无关?试讨论之。

3.22Brooks列出了5条基本设计原则:

1.设计,不要着急

2.研究其它设计

3.自顶向下设计

4.了解应用领域

5.重复

(a)解释每一条原则在编程语言设计中的意义。

(b)说明C++的设计在多大程度上符合上述设计准则(参见3.5节)。

《C语言程序设计》第三章 C语言基础 课堂笔记

页眉内容 《C语言程序设计》第三章C语言基础课堂笔记 §3.1 基本字符集、关键字和标识符 一.基本字符集 字符是C的基本元素,C语言允许使用的基本字符集: 1.26个大写字母A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2.26个小写子母a b c d e f g h I j k l m n o p q r s t u v w x y z 3.10个阿拉伯数字0 1 2 3 4 5 6 7 8 9 4.其他字符!" # % & ' ( ) * + , - . / : < = > ? [ \ ] ^ _ { | } ~ 5.空格字符以及制表符合换行符等控制字符 二.关键字(P375,附录II) C中具有固定意义的字符串。 (1) C中的关键字共32个,必须用小写字母 (2) 关键字不可用于变量名、函数名等。 auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while 三.标识符 标识符用于命名变量、类型、函数和其他各种用户定义的对象,是由字母、下划线和数字三种字符组成。 (1) 第一个字符必须为字母或下划线 (2) C对标识符的长度规定为任意,Turbo C区分32 个字符 (3) C区分大小写 (4) 不允许关键字作为标识符 §3.2 C数据类型

C语言-第三章编程题

完成以下编程题,将程序源代码粘贴提交。 P.56 2.编写程序,输入圆的半径,计算并输出其周长和面积。常量pi的值取3.14159,周长和面积取小数点后2位数字。 #include #define pi 3.14159 void main() { float r,s,a; printf("请输入圆的半径"); scanf("%f",&r); s=pi*r*r; a=2*pi*r; printf("s=%.2f,a=%.2f\n",s,a); } 3.编写程序,把整数华氏温度f转换为浮点型的摄氏温度c。转换公式为c=5/9(f-32),输出要有文字说明,取2位小数。 #include void main() { int f; float c; printf("请输入华氏温度"); scanf("%d",&f);

c=5.0/9*(f-32); printf("摄氏温度为%.2f\n",c); } 4.编写程序,输入三角形的三边的边长,求三角形面积。三角形面积的计算公式为:p=(a+b+c)/2 ,S=sqrt(p(p-a)(p-b)(p-c))。 #include #include void main() { float a,b,c,S,p; printf("请输入三角形三边长\n"); scanf("%f%f%f",&a,&b,&c); p=(a+b+c)/2.0; S=sqrt(p*(p-a)*(p-b)*(p-c)); printf("S=%f\n",S); } 6.编写程序,输入一个小写字母,输出其对应的大写字母。 #include void main() { char c; printf("请输入一个小写字母\n"); scanf("%c",&c);

二级c语言程序设计习题及解答ch1-3

12)计算机能直接执行的程序是(B )。 A)源程序B)目标程序C)汇编程序D)可执行程序 13)以下叙述中正确的是( D ) A)程序设计的任务就是编写程序代码并上机调试 B)程序设计的任务就是确定所用数据结构 C)程序设计的任务就是确定所用算法 D)以上三种说法都不完整 例年真题: #include main() { Int a; A=5; /*给A变量赋值5 A+=3; 再赋值后再加3*/ Printf(“%d”,a); } (11)以下叙述中正确的是( C )。 A)C 程序的基本组成单位是语句B)C 程序中的每一行只能写一条语句 C)简单C 语句必须以分号结束D)C 语句必须在一行内写完 (11)以下叙述中正确的是( C ) A)C程序中的注释只能出现在程序的开始位置和语句的后面 B)C程序书写格式严格,要求一行内只能写一个语句 C)C程序书写格式自由,一个语句可以写在多行上 D)用C语言编写的程序只能放在一个程序文件中 (12)以下选项中,能用作用户标识符的是( C ) A)void B)8_8 C)_0_ D)unsigned 【解析】A:关键字不可以B:不以数字开头C:正确D:关键字 (13)以下选项中合法的标识符是( C ) A)1-1 B)1—1C)-11D)1— 【解析】A:不以数字开头,出现非法字符-而不是_ B:不以数字开头,非法字符C:正确D: 不以数字开头,出现非法字符 (14)以下选项中不合法的标识符是( C ) A)print B)FOR C)&a D)_00 【解析】C:出现非法字符& (15)以下选项中,能用作数据常量的是( D ) A)o115 B)0118 C)1.5e1.5D)115L

c语言程序设计第三章课后答案

/*习题3 2*/ #include main() { int rate; double salary,tax; printf("enter salary:\n"); scanf("%lf",&salary); if(salary<=850) rate=0; else if(salary>850&&salary<=1350) rate=5; else if(salary>1350&&salary<=2850) rate=10; else if(salary>2850&&salary<=5350) rate=15; else rate=20; tax=rate*(salary-850)/100; printf("tax=%.2lf\n",tax); } /*习题3 4*/ #include main() { int n,i,A=0,B=0,C=0,D=0,E=0; double grade,sum=0,ave; printf("enter n:\n"); scanf("%d",&n); printf("enter n ge grade\n"); for(i=1;i<=n;i++) { scanf("%lf",&grade); sum=sum+grade; if(grade>=90&&grade<=100) A=A+1; else if(grade>=80&&grade<=89) B=B+1; else if(grade>=70&&grade<=79) C=C+1; else if(grade>=60&&grade<=69) D=D+1; else E=E+1;

c语言详解(第五版)第三章程序设计项目答案

c语言详解(第五版)第三章程序设计项目答案 1.假设买一辆车首付为500dollar。请计算月供。 #include #include #include int main(void) {double capital_sum,monthly_interest_rate,initial_payment,temp,payment,terms; printf("Please enter the price of the car>>"); scanf("%lf",&capital_sum); printf("Please enter the monthly interest rate>>"); scanf("%lf",&monthly_interest_rate); printf("Please enter the terms duration of loan>>"); scanf("%lf",&terms); printf("Please enter the initial_payment>>"); scanf("%lf",&initial_payment); temp=1+monthly_interest_rate; payment=(capital_sum-500)*monthly_interest_rate/(1-pow(temp,-terms)); printf("The monthly contribution is %.2f dollars.",payment); printf("The capital sum is %.2f dollars.",capital_sum); system("pause"); return 0; } 2.编写两个函数,一个显示三角形,另一个显示矩形。 #include #include #include void draw_triangle(void); void draw_rectangle(void); int main(void) {draw_triangle(); draw_rectangle(); system("pause"); return 0; } /*Draw an triangle.*/ void draw_triangle(void) { printf(" /\\ \n"); printf(" / \\ \n"); printf(" / \\ \n"); printf(" / \\ \n"); printf(" -------- \n"); } void draw_rectangle(void)

C语言程序设计(第3版)第3章习题参考答案

习题三参考答案 (1)从键盘输入一个年份值,判断是否闰年。设iYear为某一年份,iYear为闰年的条件为:iYear可以被4整除且不可以被100整除,或者iYear可以被400整除。 #include "Stdio.h" #include "Conio.h" int main(void) { int iYear; printf("please input the year:"); scanf("%d",&iYear); if(iYear%400==0||(iYear%4==0&&iYear%100!=0)) printf("%d is leap",iYear); else printf("%d is not leap",iYear); getch(); return 0; } (2)从键盘输入三个整数,按由小到大的顺序输出。 #include "stdio.h" main() {int i,j,k,max; scanf("%d%d%d",&i,&j,&k); max=i>j?i:j; max=max>k?max:k; printf("max=%d",max); getch(); } (3)假设星期一至星期五每工作一小时的工资是20元,星期六和星期日每工作一小时的工资是平时的3倍,其中工资的4.5%是税金。试编一程序从键盘输入星期序号(1,2,3,4,5,6,7,分别表示星期一至星期天)和工作小时数,计算该日的工资及应交税金。 #include "Stdio.h" #include "Conio.h" int main(void) { int iWeek,iHours ; float fSalary,fTaxes;

c语言程序设计复习题

C语言程序设计复习题 第一章C语言概述 一、选择题: 1、一个C程序的执行是从(A)。 A本程序的main函数开始,到main函数结束 B本程序文件的第一个函数开始,到本程序文件的最后一个函数结束 C本程序的main函数开始,到本程序文件的最后一个函数结束 D本程序文件的第一个函数开始,到本程序main函数结束 2、在C语言中,每个语句必须以(D)结束。 A.回车符 B.冒号 C.逗号 D.分号 3、C语言规定:在一个源程序中,main函数的位置(C)。 A.必须在最开始 B.必须在系统调用的库函数的后面 C.可以任意 D.必须在最后 4、一个C语言程序是由(B)。 A.一个主程序和若干子程序组成 B.函数组成 C.若干过程组成 D.若干子程序组成 5、下列说法中错误的是(D)。 A.主函数可以分为两个部分:主函数说明部分和主函数体 B.主函数可以调用任何非主函数的其他函数 C.任何非主函数可以调用其他任何非主函数 D.程序可以从任何非主函数开始执行 6、用C语言编写的源文件经过编译,若没有产生编译错误,则系统将(C)。 A.生成可执行目标文件 B.生成目标文件 C.输出运行结果 D.自动保存源文件 二、填空题: 1、C语言只有(32)个关键字和(9)种控制语句。 2、每个源程序有且只有一个(main)函数,系统总是从该函数开始执行C语言程序。 3、C语言程序的注释可以出现在程序中的任何地方,它总是以(\*)符号作为开始标记,以(*/)符号作为结束标记。 4、C语言中,输入操作是由库函数(scanf)完成的,输出操作是由库函数(printf)完成的。 5、系统默认的C语言源程序文件的扩展名是(.c),经过编译后生成的目标文件的扩展名是(.obj),经过连接后生成的可执行文件的扩展名是(.exe)。 6、C语言的标识符只能由(字母、数字和下划线)三种字符组成。 第三章数据类型、运算符和表达式 一、选择题: 1、以下选项中,不正确的C语言浮点型常量是(C)。 A.160. B.0.12 C.2e4.2 D.0.0 2、以下选项中,(D)是不正确的C语言字符型常量。 A.'a' B.'\x41' C.'\101' D."a"

程序设计基础-c语言-第三章程序结构-教材习题答案-科学出版社

程序设计基础-C语言(科学出版社教材) 第三章-程序结构教材习题答案 1.0编写程序使整形变量:a=3,b=4,c=5,p=0xfffe,q=0xffff;浮点型变量:x=1.2,y= 2.4,z= 3.6;无符号型变量:u=5127486,n=128765,字符型变量:c1=’a’,c2=’b’; #include void main() { int a,b,c,p,q; float x,y,z; unsigned int u,n; char c1,c2; a=3;b=4;c=5;p=0xfffe;q=0xffff; x=1.2;y=2.4;z=-3.6; u=5127486;n=128765; c1='a';c2='b'; printf("a=%d b=%d c=%d\n",a,b,c); printf("p=%#x q=%#x\n",p,q); printf("x=%f,y=%f,z=%f\n",x,y,z);

printf("x+y=%.2f y+z=%.2f x+z=%.2f\n",x+y,y+z,x+z); printf("u=%8u n=%8u\n",u,n); printf("c1='%c' or %d\n",c1,c1); printf("c2='%c' or %d\n",c2,c2); } 2.0 读入三个双精度数,求出它们的平均值并保留此平均值小数点后二位,最后输出结果。 #include void main() { double a,b,c,d; printf("请输入三个双精度数:"); scanf("%lf,%lf,%lf",&a,&b,&c); d=(a+b+c)/3; printf("d=%.2lf",d ); } 3.0 编写一个程序,要求输入三个整数值a,b,c,把c中的值赋给b, 把b中的值赋给a, 把a中的值赋给c,最后输出a,b,c.

(完整版)《C语言程序设计》复习参考答案

第一章 【随堂练习1-2】 1.下面哪些标识符属于合法的用户自定义标识符: Main、void、_num、my$、a*、N4、3m、a-2 答:合法的用户自定义标识符是:Main、_num、N4 2.结合【例1.2】指出程序代码中所用到的标识符哪些是关键字,哪些是预定义标识符,哪些是用户自定义标识符。 答:关键字:void、int 预定义标识符:include、main、printf、scanf 用户自定义标识符:a、b、c 3.分析【例1.2】中函数的结构,包括函数首部(函数返回值类型、函数名、形式参数)、函数体语句(说明语句、可执行语句)。 答:函数首部:void main( ),其中函数返回值类型为void、函数名为main、形式参数无; 函数体语句:{}内的语句为函数体,其中:说明语句为int a,b,c;,其余为可执行语句。 3.标识符是用来标识程序中的某个对象名字的字符序列。C语言把标识符分为三类,即关键字、预定义标识符、用户自定义标识符。对于用户自定义标识符的命名C语言规定: (1)所有的用户标识符必须先定义后使用; (2)用户标识符由字母(A~Z,a~z)、数字(0~9)、下划线“_”组成,并且首字符不能是数字; (3)区分大小写; (4)不能用关键字作为用户自定义标识符,通常不使用预定义标识符作为用户自定义标识符。 4.理论上讲,程序开发过程分为四个步骤,分别为: (1)编辑源程序 (2)编译源程序,生成目标程序 (3)连接目标程序及其相关模块,生成可执行文件 (4)运行可执行文件

一.单元练习 一.选择题 1.构成C语言程序的基本单位是()。 A.框架B.预处理C.函数D.语句 2.在程序开发过程中,把文本文件格式源程序转化为二进制格式的目标程序的过程称之为()。 A.编辑B.编译C.连接D.运行 3.关于主函数,下列说法不正确的是()。 A.一个完整的C语言应用程序有唯一的主函数 B.主函数的名称只能是main C.主函数可以被其他自定义函数调用 D.C语言程序的运行从主函数开始,以主函数为核心展开 4.关于标识符,下列说法不正确的是()。 A.库函数名称为预定义标识符,不建议用作用户自定义标识符 B. 关键字不能作为用户自定义标识符 C.用户自定义标识符中不区分大小写字母 D.标识符中可以出现下划线,且可以出现在标识符的任意位置 5.以下可用作用户自定义标识符的一组是()。 A.void、return、if B.printf、include、fabs C.Max、_abc、Main D.2abc、pay$、sum-10 二.填空题 1.C语言程序一般由若干个函数构成,程序中应至少包含一个_________,其名称只能为_________。 2.C语言程序中每条语句必须以_________结束。 3.C语言程序的注释是以_________开头,以________结束的,在VC++6.0编程环境中,可使用_________作为注释的起始标识,注释对程序的执行不起任何作用。 4.最初编写的C语言程序称为_________,其扩展名为_________,编译后生成的文件为_________,其扩展名是_________,连接后生成的文件是_________,其扩展名是_________。5.C语言规定,标识符只能由_________、_________和_________三种字符组成,而且,首字符只能是_________或_________。

C语言程序设计试题集与答案解析

第一章基础知识 一.填空 1. 每个C程序都必须有且仅有一个________ 函数。 2. C语言程序开发到执行通常要经过6个阶段即编辑、预处理、________、链接、加载 和执行。 3. 软件是程序,以及______、使用和维护所需要的所有文档。 4. 国标中规定:“计算机程序是按照具体要求产生的适合于计算机处理的_________”。 5. 程序设计语言按照书写形式,以及思维方式的不同一般分为低级语言和________两大类。 6. C语言是由________组成的。 7. C语言的函数可分为主函数main、标准库函数和_________。 8. 一个函数是由两部分组成的,即:________和函数体。 9. 编译是将C语言所编写的源程序________成机器代码,也称为建立目标代码程序的过程。 10. 程序是由某种程序设计语言编制出来,体现了编程者的控制思想和对计算机执行操作 的要求。不同的任务功能,就会需求不同的软件程序,如:控制计算机本身软硬件协调工作,并使其设备充分发挥效力,方便用户使用的系统软件程序,称为操作系统;而为办公自动化(OA)、管理信息系统(MIS)、人工智能、电子商务、网络互联等等应用而开发的软件程序,统称为_________。 11. 机器语言是以__________形式表示的机器基本指令的集合,是计算机系统唯一不需要翻译可以直接识别和执行的程序设计语言。 12. 与机器语言相比,使用汇编语言来编写程序可以用_______来表示指令的操作码和操作对

象,也可以用标号和符号来代替地址、常量和变量。 13. 在编译程序之前,凡以____开头的代码行都先由预处理程序预处理。 14. C程序的执行均是由执行_________开始。 15. 函数体即为包含在{}内的部分。它分为________和为完成功能任务由若干个C 语句 组成的执行部分。 16. C语言程序中一条简单语句是以________字符作为结束符的。 17. C语言是结构化、________的程序设计语言。 18. 由于计算机硬件不能直接识别高级语言中的语句,因此,必须经过“_______程序”,将用高级语言编写的程序翻译成计算机硬件所能识别的机器语言程序方可执行。 19. 用高级语言编写的程序需翻译成计算机硬件所能识别的机器语言程序方可执行。所以 说,用高级语言进行程序设计,其编程效率高,方便易用,但_______没有低级语言高。

C语言程序设计教程第三版(李凤霞)习题答案

教材习题答案 第一章 习题 一、单项选择题 1. C 2. B 3. B 4. C 5. D 6. A 7. C 8. A 二、填空题 1. 判断条件 2. 面向过程编程 3. 结构化 4. 程序 5. 面向对象的程序设计语言 6. 基本功能操作、控制结构 7. 有穷性 8. 直到型循环结构 9. 算法 10.可读性 11.模块化 12.对问题的分解和模块的划分

习题 一、单项选择题 1. B 2. D 3. C 4. B 5. A 6. A 7. B 8.C 二、填空题 1. 主 2. C编译系统 3. 函数、函数 4. 输入输出 5. 头 6. .OBJ 7. 库函数 8. 文本 第三章 习题 一、单项选择题 1. D 2. B 3. A

5. C 6. D 7. D 8. B 9. B 10.C 11.A 12.D 13.C 14.C 15.C 16.A 17.C 18.C 19.C 20.D 21.A 22.D 23.D 24.D,A 25.D 26.A 27.B 二、填空题 1. 补码 2. 308 10 - ±

~)308 10 ,15 —6 2. 308 10 - ± (~)308 10 ,15

6 3. 逻辑 4. 单目,自右向左 5. 函数调用 6. a 或 b ( 题目有错 , 小括号后面的 c<=98 改成( c>=97&&c<=98 )就可以得到所给的答案了) 7.

8. 65 , 89 第四章 习题 一、单项选择题 1. D 2. C 3. D 4. A 5. D 6. B 7. A 8. C 9. B 10.B 二、填空题 1. 一

《C语言程序设计》教案第三章程序的控制结构—循环结构while和dowhile

《C语言程序设计》课程教案表

算法2: 分析:设想用sum作为累加器,初值为0,利用sum+=i(i依次取值为1、2。。100) 此分析需要解决的3个问题: 1)sum=0,i=1; 2)每执行一次sum+=i 后,i增1(i=i+1); 3)判断如果i小于或等于100,重新执行步骤(2),否则停止计算 main() { int i,s=0; i=1; while(i<=100)/*i<=100为循环条件*/ { s=s+i; i++; /*改变i的值*/ } printf(″s=%d″,s); } 运行结果:s=5050 说明:循环结构的三个术语: 1、循环条件:循环结构中的条件表达式如while (i<=100)其中,i<100就是循环条件。 2、循环体:在每个循环周期均要执行一次的语句序列。如while下用{ }括起来的语句序列。 3、循环控制变量:能够决定控制条件是真是假的量。如while(i<=100)中的变量i 要写出一个正确的循环结构,对控制变量要做三方面的工作: 1、对循环控制变量赋初值。 2、将循环控制变量写入正确的控制条件。 3、对循环控制变量值的更新。 使用while语句需要注意以下几点: while语句的特点是先计算表达式的值,然后根据表达式的值决定是否执行循环体中的语句。因此,如果表达式的值一开始就为“假”,那么循环体一次也不执行。 当循环体为多个语句组成,必须用{}括起来,形成复合语句。 在循环体中应有使循环趋于结束的语句,以避免“死循环”的发生。 下列情况,退出while循环 条件表达式不成立(为零) 循环体内遇break,goto

c语言程序设计第三章运算符与表达式

第三章运算符和表达式 3.1 运算符和表达式概述 使用运算符可以对运算对象(包括常量和变量)进行计算以得到计算结果。用运算符将运算对象连接成一个符合C语言语法规则的式称为表达式。另外,C语言中的表达式是可以嵌套的,即简单表达式经过运算符连接后还可以形成更为复杂的表达式。 根据运算符所连接的运算对象(操作数)的个数,可以将C语言中的运算符分为三类: (1) 单目(一元)运算符:只连接一个操作数的运算符。 (2) 双目(二元)运算符:带有两个操作数的运算符。 (3) 三目(三元)运算符:同时对三个操作数进行计算的运算符。 C语言中的表达式可以分为三类: (1) 单个常量或者单个变量是最简单的表达式。 (2) 带有运算符的表达式。 (3) 函数调用。 任何一种运算都是将一定的运算符作用于一定的运算对象上,得到预期的运算结果。所以运算对象、运算符和运算结果是运算的三大要素。 3.2 算术运算符和算术表达式 一、基本算术运算符和简单算术表达式 1. 基本算术运算符 C语言为用户提供的基本算术运算符包括:+(加)、-(减)、*(乘),/(除)、%(求余),这些运算符都是双目运算符,也即在生成算术表达式的时,基本算术运算符左右两侧都必须出现运算对象。 2. 简单算术表达式 当用基本算术运算符去连接常量或者变量时,就形成简单算术表达式。简单算术表达式的基本形式:data1 op data2。data1和data2表示某个常量或者变量,op代表上述5个基本算术运算符之一。假设有变量定义:int a=20,b=-5;则a+b、a-b、a*-2、20/-b、20%6、a%b等都是简单算术表达式。 3. 基本算术运算符使用说明 (1) +、-、*,/既可以连接整型数据,也可以连接实型数据。当参与这4个运算符的运算对

《C语言程序设计》知识点总结

C语言最重要的知识点 总体上必须清楚的: 1)程序结构是三种: 顺序结构、选择结构(分支结构)、循环结构。 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数。 3)计算机的数据在电脑中保存是以二进制的形式. 数据存放的位置就是它的地址. 4)bit是位是指为0 或者1。 byte是指字节, 一个字节 = 八个位. 概念常考到的: 1、编译预处理不是C语言的一部分,不占运行时间,不要加分号。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。 2、define PI 3.1415926; 这个写法是错误的,一定不能出现分号。 3、每个C语言程序中main函数是有且只有一个。 4、在函数中不可以再定义函数。 5、算法:可以没有输入,但是一定要有输出。 6、for循环中for语句写成 for(i=0; i<100; i++); ,这个写法是有问题的,for的后面如果出现分号,相当于for循环体是空的。 7、break可用于循环结构和switch语句。 8、逗号运算符的级别最低,赋值的级别倒数第二。 第一章C语言的基础知识 第一节、对C语言的基础认识 1、C语言编写的程序称为源程序,又称为编译单位。 2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。 3、一个C语言程序有且只有一个main函数,是程序运行的起点。 第二节、熟悉vc++ 1、VC是软件,用来运行写的C语言程序。 2、每个C语言程序写完后,都是先编译,后链接,最后运行。(.c---→.obj---→.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。 第三节、标识符 1、标识符 合法的要求是由字母,数字,下划线组成。有其它元素就错了。 并且第一个必须为字母或则是下划线。第一个为数字就错了 2、标识符分为关键字、保留标识符、用户标识符。 关键字:不可以作为用户标识符号。main define scanf printf 都不是关键字。容易误解的地方:If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。 保留标识符:是系统保留的一部分标识符,通常用于系统定义和标准库函数的名字。例如,以下划线开始的标识符通常用于定义系统变量,不能使用把这些标识符来定义自己的变量。虽然它们也是合法的标识符,但是用它们来做一般标识符可能会出现运行错误。 用户标识符:基本上每年都考,详细请见书上习题。 用户标识符用来为变量、符号常量、数组、函数等取名。关键字不可以作为用户标识符。 第四节:进制的转换

C语言程序设计课后答案优选稿

C语言程序设计课后答 案 集团文件版本号:(M928-T898-M248-WU2669-I2896-DQ586-M1988)

第二章 C++简单程序设计 2-10 执行完下列语句后,a、b、c三个变量的值为多少? a = 30; b = a++; c = ++a; a:32 ; b:30 ; c:32; 2-13 写一条for语句,计数条件为n从100到200,步长为2;然后用while和do…while语句完成同样的循环。 解: for循环: for (int n = 100; n <= 200; n += 2); while循环: int x = 100; while (n <= 200) n += 2; do…while循环: int n = 100; do { n += 2; } while(n <= 200); 2-17 修改下面这个程序中 的错误,改正后它的运行 结果是什么? #include void main() int i int j; i = 10; /* 给i赋值 j = 20; /* 给j赋值 */ cout << "i + j = << i + j; /* 输出结果 */ return 0; } 解: 改正: #include int main() { int i; int j; i = 10; // 给i赋值 j = 20; /* 给j赋值 */ cout << "i + j = " << i + j; /* 输出结果 */ return 0; } 程序运行输出: i + j = 30 2-18 编写一个程序,运行 时提示输入一个数字,再 把这个数字显示出来。 解: 源程序: #include int main() { int i; cout << "请输入一个数 字:"; cin >> i;

C语言程序设计第四版第三章答案 谭浩强

第三章 3.5用scanf下面的函数输入数据,使a=3,b=7,x=8.5,y=71.82,c1=ˊAˊ,c2=ˊaˊ,问在键盘上如何输入? main() { int a,b;float x,y;char c1c2; scanf("a=%d_b=%d",&a,&b); scanf("_x=%f_y=%e",&x,&y); scanf("_c1=%c_c2=%c",&c1,&c2); } a=3_b=7 _x=8.5_y=71.82 _c1=A_c2=a 3.6要将"China"译成密码,译码规律是:用原来字母后面的第4个字母代替原来的字母.例如,字母"A"后面第4个字母是"E"."E"代替"A"。因此,"China"应译为"Glmre"。请编一程序,用赋初值的方法使cl、c2、c3、c4、c5五个变量的值分别为,’C’、’h’、’i’、’n’、’a’,经过运算,使c1、c2、c3、c4、c5分别变为’G’、’l’、’m’、’r’、’e’,并输出。解: #include main() { char c1=’C’,c2=’h’,c3=’i’,c4=’n’,c5=’a’; c1+=4; c2+=4; c3+=4; c4+=4; c5+=4; printf("密码是%c%c%c%c%c\n",c1,c2,c3,c4,c5); } 运行结果: 密码是Glmre 3.7设圆半径r=1.5,圆柱高h=3,求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积。用scanf输入数据,输出计算结果,输出时要求文字说明,取小数点后两位数字。请编程序。main() {float r,h,C1,Sa,Sb,Va,Vb; scanf("%f,%f",&r,&h); C1=2*3.14*r; Sa=3.14*r*r; Sb=4*Sa; Va=4*3.14*r*r*r/3; Vb=Sa*h; printf("C1=%.2f\n",C1); printf("Sa=%.2f\nSb=%.2f\nVa=%.2f\nVb=%.2f\n",Sa,Sb,Va,Vb); }

C语言程序设计第三版谭浩强课后习题答案完整版

C语言程序设计第三版谭浩强 课后习题答案完整版 第一章 1.5请参照本章例题,编写一个C程序,输出以下信息: ************************** 值,输出其中最大值。 解: mian() {int a,b,c,max; printf(“请输入三个数a,b,c:\n”); scanf(“%d,%d,%d”,&a,&b,&c); max=a; if(max

字母是"E"."E"代替"A"。因此,"China"应译为"Glmre"。请编一程序,用赋初值的方法使cl、c2、c3、c4、c5五个变量的值分别为,’C’、’h’、’i’、’n’、’a’,经过运算,使c1、c2、c3、c4、c5分别变为’G’、’l’、’m’、’r’、’e’,并输 { 是%c%c%c%c%c\n",c1,c2,c3,c4,c5); } 运行结果: 密码是Glmre 3.9求下面算术表达式的值。(1)x+a%3*(int)(x+y)%2/4 设x=2.5,a=7,y=4.7 (2)(float)(a+b)/2+(int)x%(int)y 设a=2,b=3,x=3.5,y=2.5 (1)2.5 (2)3.5 设 量。 (1)a+=a (2) a-=2 (3) a*=2+3 (4)a/=a+a (5) a%=(n%=2),n的值等于5 (6)a+=a-=a*=a

《C语言程序设计》月考试题(第三章)

实验五班《C语言程序设计》月考试题 学号姓名 一、填空题 1、应用程序ONEFUNC.C中只有一个函数,这个函数的名称是________________。 2、在一个C源程序中,注释部分两侧的分界符分别是__________和__________。 3、C语言程序的基本单位或者模块是________。 4、C语言程序的语句结束符是________ 5、C程序中数据有和之分,其中,用一个标识符代表一个常量的,称为常量。C语言规定在程序中对用到的所有数据都必须指定其数据类型,对变量必须做到先,后使用。 6、C语言所提供的基本数据类型包括:单精度型、双精度型、、和枚举类型。 7、在C语言中的实型变量分为两种类型,它们是和。 8、在C语言中,以16位PC机为例,一个char型数据在内存中所占的字节数为;一个int型数据在内存中所占的字节数为,则int型数据的取值范围为。 9、C语言中的标识符只能由三种字符组成,它们是、和下划线。且第一个字符必须为。 10、5.4321E2在常规表示法中表示为 ,327.876在科学表示法中表 示为。 11、常量123456789123L占用字节存储空间。 12、负数在计算机中是以形式表示。 13、单精度型实数的有效位是位,双精度型实数的有效位是位。 14、C的字符常量是用引号括起来的个字符,而字符串常量是用号括起来的序列。 15、C规定:在一个字符串的结尾加一个标志‘\0’。 16、C语言中,字符型数据和数据之间可以通用。 17、假设已指定i为整型变量,f为float变量,d为double型变量,e为long

型变量,有式子10+'a'+i*f-d/e,则结果为型。 18、若有定义:char c=‘\010’;则变量c中包含的字符个数为。 19、若s为型变量,且s=6,则表达式s%2+(s+1)%2的值为。 20、在ASCII代码表中可以看到每一个小写字母比它相应的大写字母ASCII码大(十进制数)。 21、5/3的值为,5.0/3的值为。 22、自增运算符++、自减运算符--,只能用于,不能用于常量或表达式。 23、++和- -的结合方向是“自至”。 24、赋值运算符的作用是将一个数据赋给一个。 25、若x和n均是int型变量,且x和n的初值均为5,则执行下面表达式后x 的值为,n的值为。 x+=n++; 二、选择题 1、一个C程序的执行是从( )。 A)本程序的main函数开始,到main函数结束。 B)本程序文件的第一个函数开始,到本程序文件的最后一个函数结束。 C)本程序的main函数开始,到本程序文件的最后一个函数结束。 D)本程序文件的第一个函数开始,到本程序main函数结束。 2、 C语言规定:在一个源程序中,main函数的位置( )。 A)必须在程序的开头。 B)必须在系统调用的库函数的后面。 C)可以在程序的任意位置。 D)必须在程序的最后。 3、一个C语言程序是由( )。 A)一个主程序和若干子程序组成。 B)函数组成。 C)若干过程组成。 D)若干子程序组成。 4、C编译程序是 ( )。 A)将C源程序编译成目标程序的程序。 B)一组机器语言指令。 C) 将C源程序编译成应用软件。

相关文档
最新文档