![《软件详细设计教程》课件第11章_第1页](http://file4.renrendoc.com/view12/M00/35/2B/wKhkGWbUHOiAOd6QAACcvzZuZxE495.jpg)
![《软件详细设计教程》课件第11章_第2页](http://file4.renrendoc.com/view12/M00/35/2B/wKhkGWbUHOiAOd6QAACcvzZuZxE4952.jpg)
![《软件详细设计教程》课件第11章_第3页](http://file4.renrendoc.com/view12/M00/35/2B/wKhkGWbUHOiAOd6QAACcvzZuZxE4953.jpg)
![《软件详细设计教程》课件第11章_第4页](http://file4.renrendoc.com/view12/M00/35/2B/wKhkGWbUHOiAOd6QAACcvzZuZxE4954.jpg)
![《软件详细设计教程》课件第11章_第5页](http://file4.renrendoc.com/view12/M00/35/2B/wKhkGWbUHOiAOd6QAACcvzZuZxE4955.jpg)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第11章编码设计与规范11.1程序设计语言11.2编码规范11.3编码风格11.4程序效率11.5编程安全11.6小结 11.1程序设计语言
11.1.1程序设计语言的基本概念
1.低级语言和高级语言
计算机刚问世的时候,它的硬件只能识别由字符0和1组成的二进制机器指令序列,也就是最初的机器指令程序。机器指令程序是最基本的计算机语言。机器语言与特定的计算机硬件相关,要求程序设计人员对计算机的内部硬件结构要有比较多的了解。由于机器指令程序是二进制的机器指令序列,所以,它的可读性相当差,所编制出来的程序难以理解、修改和维护。为了提高程序设计的效率,人们用容易记忆的符号来代替机器指令,这些符号就是汇编指令。汇编语言是汇编指令的集合,它的使用提高了编程的效率,同时,使用汇编语言编写的程序可读性也有一定的提高。比如,汇编语言的加法用ADD表示,减法用SUB表示,乘法用MUL表示等。但汇编语言只是机器语言的简单对应,汇编语言程序设计仍然与特定的计算机硬件紧密关联,效率不高。而且,采用汇编语言编写的程序,要经汇编程序翻译成机器语言程序,才能在计算机上运行。机器语言和汇编语言都是面向机器的,通常称为低级语言。低级语言的共同点是面向机器,与机器的内部体系结构有着密切关系。不同的计算机通常有着不同的低级语言。用低级语言编码难以掌握,编程过程中容易出错,并且非专业人员难以弄懂。
与低级语言不同,高级语言是面向用户和各种实际应用的,它的抽象程度更高,功能更强大。它不依赖于某种计算机硬件,通用性强,移植性好,而且在形式上与人们使用的自然语言比较接近,易学易懂,适合于推广使用。高级语言对计算机的各种操作进行高度抽象,从而使得一个对底层硬件知之甚少的人也能用它来编写程序。但用高级语言编写的程序在执行过程中需要解释和编译程序,所以程序的运行速度较低级语言程序慢。
2.汇编程序、编译程序和解释程序
程序设计语言是一套表达计算过程的符号系统,其表达形式应该能够同时被计算机和人所理解。高级语言接近于自然语言,使得程序设计语言易于掌握和运用。特别是面向对象程序设计语言,它从客观世界所存在的对象实体出发,力求贴近人们考虑问题的实际思维模式和习惯,使得运用程序设计语言对问题空间进行描述变得更加容易。
人们可以借用各种高级语言与计算机进行交互,但对于计算机来说,它能够直接理解和执行的,仍然只能是由二进制机器指令组成的机器指令程序。因此,使用其他程序设计语言编写出来的程序代码,需要最终翻译为机器指令程序才能执行。实现这些翻译功能的程序称为语言处理程序。翻译程序就是指这样一种程序,通过它能够将使用高级语言编写的源程序转换成与之在逻辑上等价的低级语言形式的目标程序。常见的翻译程序大致可以分为汇编程序、编译程序和解释程序。
通常所说的源程序,是指程序员使用某种高级语言或汇编语言编写出来的程序代码,这些程序代码需要经过翻译才能执行。汇编程序对使用汇编语言编写出来的源程序进行翻译,得到可执行的目标程序,然后在计算机上运行。如果要运行使用高级语言编写出来的源程序,则需要相应的编译程序或解释程序,对源程序进行翻译,从而使其可以在计算机上执行。编译程序首先将源程序翻译成目标程序,然后再执行该目标程序。如果编译生成的目标程序是汇编语言形式,在编译和运行阶段之间就要添加一个汇编阶段,把经过编译生成的汇编语言目标程序汇编成机器语言目标程序。在编译方式下,机器上运行的是与源程序等价的目标程序,源程序和编译程序都不再参与目标程序的执行过程。
解释程序则是逐条读出源程序中的语句并解释执行,即在解释程序的执行过程中并不产生目标程序,解释一条语句就执行一条语句。在解释方式下,解释程序和源程序(或其某种等价表示)要参与到程序的运行过程中,运行程序的控制权在解释程序。典型的解释型高级语言是BASIC语言。编译程序和解释程序也可以组合起来工作。比如,首先由编译程序将源程序编译成中间代码,然后由解释程序对这些中间代码解释执行,这样就既包含了编译的过程,又包含了解释的过程。
3.程序设计语言的定义
程序设计语言需要完整、精确的定义。如果没有一种清楚的符号来表示语言结构的种种效果,那么我们将无法清楚地知道语言所描述的到底是什么,更无法从数学的角度系统地对程序的效果进行推理。另外,要实现高级语言与计算机内部体系结构无关,应该使用一种标准的方式来定义语言,使定义能够被全世界所接受。比如标准化组织ANSI和ISO就已经对很多程序设计语言进行了严格的定义,这些语言包括Pascal、FORTRAN、C、C++、Ada、Prolog等。程序设计语言的定义一般包括3个方面:语法、语义和语用。
程序设计语言的语法和我们通常所使用的自然语言语法有很多相通之处,它决定了一些语言要素如何组合在一起构成其他语言要素的规则,即由程序语言的基本符号组成程序中的各个语法成分的一组规则。这些规则包括词法规则和语法规则。其中,词法规则是指由基本符号构成的符号(单词)书写规则,类似于自然语言中的单词拼写法。语法规则是指由符号(单词)构成语法成分的规则,类似于自然语言中的遣词造句,甚至组段谋篇。程序设计语言的定义是严格的,其语法可通过形式语言进行描述。几乎所有的程序设计语言都使用上下文无关文法进行语法定义。
程序设计语言的语法是程序设计语言的有机组成部分,规定了程序的外观结构。与语法相比,程序设计语言的语义更加复杂,难以用精确的方法进行描述。首先,人类语言本身的丰富性就已经使得“语义”可以从不同的角度去理解,从而得到不同的结果。另外,许多语句的含义是与上下文环境有关的,在不同语境中的含义并不完全相同。程序设计语言的语义是程序设计语言中按语法规则构成的各个语法成分的含义。其中,编译时就可以确定的语法成分的含义称为静态语义,运行时刻才能确定的含义称为动态语义。一个程序的语义决定了该程序执行的效果,它本身取决于构成该程序的各个组成部分的语义。
语义主要是表明构成语言的各个符号和使用者的关系,与符号的来源、使用和影响有关。语言实现的语境则是指理解和实现程序设计语言的环境,这种环境主要包括程序的编译环境和运行环境。11.1.2程序设计语言的发展及种类
1.程序设计语言的发展
从计算机开始得到广泛应用至今,程序设计语言也经历了很大的变化,种类已达上百种。今后程序设计语言将会继续发展完善,从一开始的机器语言,到后来的汇编语言,再到现在的高级语言,并向更高级语言过渡。
程序设计语言有交流算法和计算机实现的双重目的,它用于描述计算机上的运算,其历史系于20世纪40年代起步的电子计算机的发展史。另外,程序设计语言还有一个相对独立于机器的发展史,该历史系于数学及数学概念的发展,主要焦点集中在程序设计语言的第二种重要功能:计算和算法的描述。
1)第一位程序员
完全用于计算的第一台计算机是巴贝奇(Babbage)于1830~1840年发明的,通常称为巴贝奇分析机。虽然该机器没有完全建成,但它却能执行若干计算实例。这些实例是由著名诗人拜伦的女儿Ada设计的,因而她被公认为是第一个程序员。后来人们用她的名字命名为Ada语言。她对计算机的概念提出了许多深刻而影响深远的见解。
2)最初的程序设计语言
20世纪50年代初,具有存储程序的通用电子数字计算机问世。早期的程序是直接用二进制机器代码编写的。接着,很快就产生了能用符号和助记符表达机器代码的汇编语言。
FORTRAN(FormulaTranslation)是第一个被广泛用来进行科学计算的高级语言,在1954年至1957年间由JohnBackus领导下的IBM的一个开发小组研制出来。一个FORTRAN程序由一个主程序或一个主程序和若干子程序组成,主程序和子程序都是独立的程序单位,称为程序模块。子程序是实现模块化的有效途径。FORTRAN也能用于通用程序设计,它的编译程序效率较高,是一种影响比较深远的程序设计语言,至今仍在某些科学应用领域发挥着重大的影响力。FORTRAN的一些特性已经成为语言的标准,如数组、由索引变量控制的循环结构、if分支语句等。
Algol60是于1958~1960年研制出来的一种用于科学研究和实际应用的通用程序设计语言。如果程序设计语言能够应用于范围广泛的问题求解过程中,我们就称之为通用程序设计语言。Algol60语言主导了20世纪60年代的程序语言的发展,它支持算法描述,在欧洲拥有众多忠实的用户。该语言具有严格的文法定义,采用巴克斯范式(BNF)来描述语言的语法,同时在程序设计中引进了许多概念,如自由格式、结构化语句、begin-end块、变量类型说明、递归、传值参数等。许多命令式语言都是由Algol60语言派生而来,如Pascal、C、Ada等。
COBOL(COmmonBusinessOrientedLanguage)是一种面向事务处理的高级语言,于1959~1960年由美国国防部的一个研制小组在GraceHopper的领导下研制出来。COBOL语言适用于数值计算并不复杂、数据处理的信息量却很大的领域,比如企业管理。目前,COBOL语言主要应用于情报检索、商业数据处理等领域。它的语法规则很严格,使用了300多个英语保留字,大量采用普通英语词汇和句型。尽管COBOL语言语法复杂,但它仍不失为一种实用的语言,在相关领域的应用一直非常广泛。以上三种高级语言是基于冯·诺依曼计算机体系结构标准创建的。有一些语言是在数学函数的基础上开发的,如LISP和APL语言。
LISP(表处理器)是20世纪50年代末JohnMcCarthy设计的基于通用表结构及函数应用的语言。LISP多应用于人工智能领域,它引入了垃圾回收(或无用内存回收)等机制。APL是20世纪50年代末Harvard大学的K. Iverson开发的一种程序设计语言。这种程序设计语言的运算能力强大,20世纪60年代初曾被IBM作为处理数组和矩阵等数学计算的程序设计语言。这两种函数式语言也对后来的程序设计语言产生了极大的影响。
3)程序设计语言的迅速发展
自从最初的程序设计语言取得巨大成功后,越来越多的人都想涉及该领域,这直接导致了新的程序设计语言的大量涌现。这些语言尽管大多数现在已不复存在,但它们无疑极大地推动了程序设计语言的进一步发展和完善。20世纪60年代出现的语言中,影响比较大的主要有Simula67和BASIC。
Simula67由挪威的KristenNygaard和Ole-JohanDahl于1965~1967年研制出来。
Simula67最初是为了模拟离散事件而设计的,它的基本贡献是对抽象和计算的理解,它引入了类的概念,这是大多数面向对象程序设计语言的基础。Simula67被认为是第一个面向对象程序设计语言。
BASIC(BeginnersAll-purposeSymbolicInstructionCode)是由Dartmouth大学的JohnKemeney和ThomasKurtz于1964年设计的。BASIC最初是为分时系统而开发的一种简单语言,随后逐渐过渡到微型计算机领域,并且得到了广泛的应用。ANSI分别于1978年和1988年颁布了BASIC的最简标准和最高标准。
经过20世纪60年代的混战,语言设计者又重新开始考虑语言设计的简单性和一致性问题。20世纪70年代的代表之作是Pascal语言和C语言。
Pascal语言是一种结构化程序设计语言,由瑞士苏黎世联邦工业大学的NiklausWirth教授研制,并于1971年正式发表。它将Algol语言的思想演化成为更适合于教学的简洁、有效的结构化语言。一个Pascal程序可以看做一个操作系统调用的过程,而且其内部的过程还可以嵌套和递归。尽管Pascal语言比较简单,并不具备足够的实用特性,但它在教学和许多实际应用方面仍取得了很大的成就。
C语言是由贝尔实验室于1972年设计出来的一种通用程序设计语言。C语言提供了一个丰富的运算符集合和比较紧凑的语句格式。C语言在许多方面比Pascal语言简单,它简化了类型系统和运行环境的复杂性,提供了更多访问底层机器的途径。C语言的主要特色是兼顾了高级语言和汇编语言的特点,简洁、丰富、可移植。C语言的成功与操作系统紧密相关。它提供了高效的执行语句,并且允许程序员直接访问操作系统和底层硬件。广泛流行的UNIX操作系统及其上的许多软件都是用C语言编写的。相对于高级语言,C语言有时也被称为中级程序设计语言。
C++ 语言是在C语言的基础上发展起来的,与C语言兼容。它是1980年由贝尔实验室开发出来的。C++ 语言主要是在C语言的基础上增加了类的机制,从而使其成为一种面向对象程序设计语言。它一开始也被称为“含类库的C”,增加了大容量的类库等许多新特性,并具有较高的执行效率。而且,由于C语言的流行和可移植,C++ 语言事实上可以移植到任何平台上运行。相关国际标准的制定更促进了C++ 语言的广泛应用。但是,由于C++已经成为一个庞大的语言系统,反而显得比较难以实现和彻底理解。
Java产生于20世纪90年代,它一开始被命名为Oak,是Sun公司的JamesGosling等人为嵌入式电子应用系统而开发的一种高级语言。随着Internet应用的增长及Web的出现,Sun的开发者们为适应Web和网络应用,将其修改完善并定名为Java。Java主要是面向Internet应用的,但作为一种通用的程序设计语言,也得到了广泛的应用。它保留了C++ 的基本语法、类和继承等概念,删掉了C++ 中一些不好的特征,是一种纯粹的面向对象语言,其结构简单、设计简洁、可移植性好。与C++ 相比,Java更加简单,其语法和语义更加合理。如果只考虑通用语言而不考虑专用语言,则程序设计语言的发展过程可大致描述为如图11.1所示的形态。面向机器程序设计语言是第一代程序设计语言,最初出现的程序设计语言是第二代程序设计语言,而结构化过程语言和面向对象语言则属于第三代程序语言,第四代程序设计语言与以往的程序设计语言相比,将更加抽象和智能化。图11.1程序设计语言的发展
2.程序设计语言的种类
程序设计语言的分类没有统一的标准。根据程序设计语言的发展,大体上可以将其分为面向机器语言、高级语言和第四代语言三大类。
1)面向机器语言
面向机器语言依赖于具体的硬件结构,其语句与计算机的硬件操作相对应,主要是指二进制的机器语言和汇编语言。
2)高级语言
(1)基础语言。高级语言创始于20世纪50年代。一开始的这些程序设计语言出现早、应用广泛、有大量软件库,为早期的程序员广泛接受和熟悉,所以称为基础语言。其中,BASIC、FORTRAN、COBOL、ALGOL是该类语言的代表。这些语言中有一些随着版本的更新与性能的改进,至今仍被应用。
(2)结构化语言。结构化语言提供结构化的控制结构语句及数据类型定义语句,具有很强的过程描述能力和数据结构描述能力。它控制结构完备,数据结构和数据类型丰富,表达能力强,所编出来的程序模块清晰,可移植性好。比较有代表性的结构化程序设计语言有Pascal、C等。
(3)面向对象语言。面向对象语言可以分为两大类:一类是纯面向对象语言,如Smalltalk、Eiffel、Java等;另一类是混合型面向对象语言,这类语言在面向过程语言的基础上增加了面向对象的机制,如C++ 等。成熟的面向对象语言通常都提供丰富的类库和强有力的开发环境。纯面向对象语言着重支持面向对象语言方法研究和快速原型的实现;混合型面向对象语言一方面注重提高程序的运行速度,另一方面又要使得传统的程序员能够比较容易地接受面向对象的思想。
(4)专用语言。专用语言是为某种特殊应用而设计的程序设计语言,这类语言的应用范围一般比较窄。例如,APL是为数组和向量运算而设计的语言,BLISS是为开发编译程序和操作系统而设计的语言,L1SP和PROLOG两种语言主要运用于人工智能领域。
3)第四代语言
也许有一天计算机有能力去理解人们用自然语言描述的软件需求,这将极大地简化软件的开发工作。从程序设计语言的发展趋势也可以看出,人们一直在不断寻求使用越来越抽象的形式来表示程序,从而把程序员从复杂的过程性细节中解放出来。第四代语言(4GL)就是这样一种语言,它在更高一级抽象的层次上表示程序结构和数据结构,不再需要规定算法的细节,而由其语言系统运用专门的领域知识来填充过程细节。现有的第四代语言主要有数据库查询语言、程序生成器、判定支持语言、原型语言、形式化规格说明语言等。程序设计语言根据设计程序的办法又可以分为命令式程序设计语言、面向对象程序设计语言、函数式程序设计语言和逻辑型程序设汁语言。
(1)命令式程序设计语言。命令式程序设计语言是一种基于动作的语言,它把计算看成是一种动作的序列。命令式程序设计语言开始于FORTRAN,其关键思想在Pascal和C语言中也得到了很好的体现。
(2)面向对象程序设计语言。面向对象程序设计语言将状态和操作封装于对象实体之中,并将具有相同状态、操作和访问机制的多个对象抽象成一个对象类。类是面向对象语言中必须提供的、由用户定义的数据类型。面向对象语言具有继承、封装、多态等特征,有利于软件的实现和重用。Smalltalk、C++、Java是面向对象程序设计语言的代表。
(3)函数式程序设计语言。函数式程序设计语言的基本概念来自于LISP语言,它是一类以λ演算为基础的语言。LISP语言是1958年专门为人工智能的应用而设计的语言。在这种语言里,函数是一种映射规则,它使定义域中的每个元素和值中唯一的元素相对应,而且可以在一个函数定义里调用另一个函数。函数式程序设计语言的特点之一是,表达式中出现的任何函数都可以用其他函数来代替,只要这些函数调用产生相同的值。
(4)逻辑型程序设计语言。逻辑型程序设计语言是一类以形式逻辑为基础的语言,以PROLOG语言为代表。PROLOG表示“ProgramminginLogic”,这种语言建立在关系理论和一阶谓词逻辑的基础之上,和LISP语言一样多用于人工智能领域。PROLOG程序是一系列事实、数据对象或事实间的具体关系和规则的集合。PROLOG语言的关键是模式匹配,通过匹配一组变量与预先定义好的模式和规则来完成操作。该语言具有很强的推理功能,适用于自动定理证明、自然语言理解及专家系统等。从语言的内在特点进行分类,则程序设计语言还可以分为系统实现语言、静态语言、块结构语言和动态语言四类。
(1)系统实现语言。系统实现语言从汇编语言发展而来,它提供高级语言功能,同时容许程序员直接针对计算机进行操作。C语言是一种系统实现语言。
(2)静态语言。静态语言的主要特点是静态地分配存储空间,这种存储分配方式的优点是方便编译系统的设计和实现,但相应地就给程序员增加了许多限制。比较著名的静态语言有FORTRAN、COBOL等。
(3)块结构语言。块结构语言的特点是以程序块为单位,提供一种有限形式的动态存储分配。每当进入一个程序块,就中断程序的执行,由存储管理系统为其分配存储空间,退出时则释放该存储空间。块结构语言有PASCAL、ALGOL等。
(4)动态语言。动态语言支持动态存储管理,这种语言在执行个别语句的时候就可能引起存储的分配和释放。11.1.3程序设计语言的基本成分
1.数据
程序设计语言的数据成分对应程序设计语言里的数据类型。数据作为程序操作的对象,总是对应着应用系统中某些有意义的实体。数据具有名称、类型、存储类别、作用域和生存期等属性,数据的表示指明了程序中值的组织形式,使用时要为数据分配存储空间。
数据名称是用户命名的由字母、数字和下划线组成的标识符。数据类型说明数据占用内存空间的大小和存储形式。存储类别说明数据在内存中的位置和生存期。作用域说明数据的作用范围,即可以使用数据的代码范围。生存期说明数据占用内存空间的时间。
(1)常量和变量。常量的值在程序运行时不能改变,变量的值在程序运行时可以改变。程序中的数据对象在内存中表现为存储单元的地址和内容,分别对应于数据对象的左值和右值。当数据的值改变时,实际上改变的是数据对象的右值。
(2)全局量和局部量。全局量和局部量的区别主要是数据作用域的范围不同。在程序运行过程中,系统为全局变量分配的存储空间一般是不改变的,而为局部变量分配的存储空间则是动态改变的。
(3)数据类型。数据类型一般可以分为基本类型、构造类型、用户自定义类型等。数据类型不同,数据的组织形式也会有所区别。
在C++ 语言里,基本数据类型包括整型(int)、字符型(char)、实型(float、double)和布尔型(bool)。指针类型(type*)是一种比较常用的数据类型。空类型(void)是一种特殊类型。枚举类型(enum)是一种用户自定义类型。而构造类型包括数组、结构和联合。另外,类类型是一种抽象数据类型。
2.运算
程序设计语言的运算成分主要包括运算符号和运算规则的规定。高级语言的运算一般可分为数值运算、关系运算和逻辑运算。运算符号的使用与数据类型密切相关,而且要规定优先级和结合性,以确保运算结果的唯一性。
3.控制
程序语言的控制成分是指程序语言所允许表述的控制结构。理论证明,可计算问题的程序都可以用顺序结构、选择结构和循环结构来描述。
(1)顺序结构。顺序结构用来表示一个顺序计算操作序列。用顺序结构表述的计算过程从执行第一个操作开始,按顺序依次执行所有后续操作,直至该执行序列的最后一个操作。顺序结构示意图如图11.2所示。
顺序结构在C++ 里可以表现为复合语句,也就是由“{”和“}”括起来的程序语句。复合语句在程序中作为一个整体来执行。图11.2顺序结构
(2)选择结构。选择结构提供了在两个或多个分支中选择其中一个或多个的逻辑。选择结构在程序设计语言中用条件语句来实现。选择结构首先需要指定一个条件,根据条件的成立与否决定控制流走向哪一个程序分支,其示意图如图11.3所示。选择结构可以嵌套其他结构及其自身。简化的选择结构可以没有程序块B,即:如果条件成立,则执行A;如果条件不成立,则什么也不做。图11.3选择结构在C++ 里选择结构用if语句和switch语句实现。
if语句的一般形式为:
if(表达式)语句lelse语句2
当语句2为空时,可以简化为:
if(表达式)语句1
if语句是一种双分支结构,当表达式成立时,执行语句1;当表达式不成立时,执行语句2或什么也不执行。执行switch语句时,首先计算表达式的值,将其与所列举的常量表达式的值比较。如果匹配,则执行后面的语句;如果该值与所有的常量表达式的值都无法匹配,则执行default后面的语句。可以使用break跳出switch语句。
(3)循环结构。循环结构描述了重复计算的过程。循环结构通常包括三部分:初始化、循环条件和循环体。初始化是在进入循环体之前,对在循环体中所要用到的变量赋初值,有时并不需要显式表示。循环条件是程序重复执行所必须满足的约束条件。循环体是重复计算的内容。循环结构主要有两种形式。一种是先判断条件是否成立,如果成立则执行循环体,然后再次判断循环条件是否满足,而进一步确定是否进入下一次循环,这称为while型循环结构。另一种是do-while型循环结构,它的执行过程是:先进入循环体执行程序块,然后判断条件是否成立,如果条件成立则继续执行循环体,进入下一次循环判断,否则停止执行,退出循环结构。显然,与while型循环结构不同的是,do-while型循环结构先执行一次循环体,然后再对循环条件进行判断,也就是说,在do-while型循环结构里,循环体至少要被执行一次。循环结构的示意图分别如图11.4和图11.5所示。图11.4while型循环结构图11.5do-while型循环结构函数首部包含函数的返回值类型、函数名以及函数运行所需要的参数及其类型。函数体包含实现函数功能的程序语句。另外,C++ 程序中所有函数的定义都是独立的,不允许函数的嵌套定义。函数的使用也要遵循先声明后使用的原则。如果对函数的调用在函数定义之前,则应先声明函数。函数声明的一般格式如下:
返回值类型函数名(形式参数表);
函数声明定义了函数的原型,规定了传递给函数的参数个数、参数类型以及函数返回值类型。编译器可以据此对函数的调用进行初步检查,以确定调用是否正确。函数声明必须与函数定义一致。函数调用只需知道被调用函数所能实现的功能以及函数调用的方法,而无需关心函数实现的内部细节。函数调用的一般形式为:
函数名(实际参数表);
可见,要调用一个函数,需知道该函数的函数名、传递的参数(个数、类型和顺序)等信息。函数间的信息交换主要是靠返回值及参数传递来实现。使用返回值时,被调函数执行以后,把结果返回给主调函数。参数传递时实参与形参的信息交换方法主要有传值调用和引用调用两种。传值调用时,实参向形参传递相应类型的值,函数对所传递的值进行运算,但不能向实参返回任何信息。通常可以使用指针作为形参,这样,当函数调用时,首先对实参进行取地址运算,将实参地址传递给形参,从而实现对实参的修改。这是一种间接内存访问形式。
引用调用时,将实际参数的地址传递给形式参数,使得形参的地址就是对应的实参的地址。这时候,形参相当于是实参的别名,函数中对形参的访问和修改也就相当于对相应的实际参数所作的访问和修改。一个类类型中可以有两种成员:数据和操作,它们在C++ 语言中被称为数据成员和成员函数。根据它们的被访问权限,类的成员又可以分为私有段成员、保护段成员和公有段成员,分别对应于段约束符private、protected和public。其中,private标识的部分为私有段,它们只能被类的成员函数所访问,private标识符可以缺省;由protected标识的部分为保护段,它们能被本类及其派生类的成员函数所访问;由public标识的部分为公有段,它们可以被其他类的对象所访问,构成该类类型对象的访问接口。另外,在类里,定义公有和私有成员的顺序可以是任意的。11.1.4程序设计语言的要素
1.命名约定
名字的声明,即预先说明程序中使用的对象的名字。某些程序设计语言要求“先声明、后使用”,如C语言。这样就使得编译程序能检查程序中所出现的名字的合法性,从而帮助程序员发现和改正程序中的错误。也有一些程序设计语言,如FORTRAN和BASIC,并不要求用户显式地说明程序中所有对象的名字,当第一次使用一个名字来表示一个对象时,则自动地被编译系统看做是对该对象的名字的声明。这样表面上看起来使用非常方便,但实际上更加容易出错。在输入源程序时,如果程序员输入了错误的对象名字,这时候它最有可能将被编译系统理解为新定义的对象,而不会报告错误,这样造成的程序错误是很难发现的,特别是当输入错误的字符和预定要使用的字符非常相像(如字母o和数字0,字母l和数字1等)的时候。因此,现在尽管有一些程序设计语言(如VBScript脚本语言)并不要求显式说明其所用到的对象的名字,但我们应尽可能地遵循“先声明,后使用”的约定。
2.类型说明
类型说明与名字的声明紧密相联,一般在声明对象名字的同时说明对象的类型。在名字声明的时候,通过类型说明,用户定义了对象的类型,从而确定了该对象的使用方式和存储方式。这样,编译系统就能够在编译时发现对一个特定类型对象的使用是否合法,从而减少程序错误。规定必须在使用前预先说明对象的类型,使对象的定义更加明确,也有助于减少阅读程序时的歧义性。有些程序设计语言允许用户不对变量的类型进行显式的说明,给程序的安全性留下了隐患。程序设计语言中的类型说明不仅仅是一种安全措施,也是一种重要的抽象机制。为了适应软件设计和编程的需要,某些程序设计语言(如Pascal等)还允许程序员在程序设计过程中定义与特定应用有关的自己的数据类型。用户既可以定义整型、字符型等简单类型,也可以定义链表、二叉树等复杂的结构类型。面向对象的程序设计语言中的类,从本质来说,也是一种由用户自定义的数据结构类型。它允许把各种数据类型及对其进行的操作封装成一个整体(即类),供程序使用。对类型名字的定义使得我们可以引用某些复杂的实体,而不必考虑这些实体的表示方法。
3.变量初始化
在使用变量前没有对变量进行初始化是程序中最常见的错误之一。最有效地减少程序发生错误的方法是,在声明变量时对程序中的所有变量进行初始化。但大多数程序设计语言通常都无法对此进行检查和约束,更没有办法强制程序员一定要这样做。以下是一个有效的解决办法:声明变量时,如果尚未初始化,首先由系统给变量赋一个标明变量还没被初始化的特殊的值,以后如果没有对这个变量赋值就企图利用它的值,则系统应能发出出错信号,以提示用户对该变量进行有效的初始化。
4.程序对象局部化
程序对象局部化是程序设计的一个一般原理。程序对象的名字应该在靠近使用它们的地方引入,并且在程序真正需要它们的地方才具有可见性(即可访问它们)。程序设计语言的局部性按其提供局部变量的途径可分为单层局部性和多层局部性。如FORTRAN等语言提供的是单层局部性,块结构语言提供的是多层局部性。
多层次的局部性有助于提高程序的可读性。如果程序对象名字的特性在靠近使用这些名字的地方进行说明,程序的阅读者就能很容易地获得有关这些名字的信息。多层次局部性也有助于减少程序中所存在的差错和提高程序的可修改性。具有多层次局部性的程序设计语言通常鼓励程序员尽量使用局部的对象。在块结构语言中,如果内层结构和外层结构使用了相同的变量名,那么外层名字在内层是不可见的,内层不能访问外层的同名变量。这样一方面给程序的编写提供了方便,另一方面也往往会由于疏忽而引起错误。特别是在程序维护时,如果维护人员不是程序的设计人员,则更容易出现这种差错。使用具有单层局部性的程序设计语言写出来的程序可读性要差一些,但是却容易实现程序单元的独立编译。
5.程序模块
在块结构语言中,在较内层程序块中说明的名字不能被较外层的程序块访问,这是块结构语言控制程序对象名字可见性的主要手段。由于是动态存储分配,当调用完一个程序模块时,局部对象的值当即被释放,如果需要在多次调用这些子程序的过程中保存和传递对象的值,则须把对象说明成全局的,也就是程序中所有的子程序都可以访问。程序模块之间的数据共享可以通过定义全局变量来提供,然而这却降低了程序的安全性,同时也增加了程序维护时发生错误的可能性。有些语言(如C、C++ 语言等)通过静态变量提供共享,这就很好地解决了这个问题。
6.循环控制结构
循环控制结构通常可以分为三种情况。一种是给定循环次数,当计数结束时即结束循环。还有一种是在每次进入循环体之前测试循环结束条件,根据该条件决定是结束循环还是进入下一轮循环。又或者每执行完一次循环体测试一次循环结束条件,以判断是否结束循环。在实际应用中,有时还需要在循环体内任意一点测试循环结束条件,以做出相应处理,如提前跳出循环。在C或C++ 语言中,循环控制结构可以用for语句、while语句和do-while语句表示。在循环体内可以使用break语句或continue语句,或跳出循环,或忽略循环体的执行而直接进入下一次循环。在C语言中还提供了goto语句,以实现程序控制流的跳转,但由于它会破坏程序的逻辑结构,因此在程序设计中并不提倡使用。
7.分支控制结构
分支控制结构是一种选择结构,在两种或多种分支中,根据程序所提供的条件的满足情况选择其中的一个或多个逻辑。分支控制结构在程序设计语言中通常有if型和case型。if语句比较简单,常用在分支比较少的情况下。多分支的case语句则可能会存在以下一些问题。如果case表达式的取值不在预先定义的范围内,则无法决定程序应该做的动作。在某些程序设计语言中,case表达式选定执行的语句与所有可能执行的case语句的排列次序有关,如果语句的次序不正确,则可能会出现意想不到的结果,而且系统编译和运行时并不能发现此类错误。在C或C++ 语言中,只有当case表达式的值与case标号相匹配,才选择相应的语句来执行,case中没有定义的情况,则在default标号下进行处理,这样即很好地解决了以上问题。
8.异常处理
异常是指程序运行过程中发生的错误或意外事件,程序必须对此类事件做出必要的处理,否则将导致程序的中断和其他意想不到的后果。多数程序设计语言在检测和处理异常方面给程序员提供的帮助非常之少,程序员只能使用语言提供的一般控制结构检测异常,当发生异常时,把控制转移到处理异常的程序段。这样,当程序中包含一系列子程序的嵌套调用时,需要把出现的异常信息从一个子程序传送到另外的子程序,但是要完成此任务并没有方便而又可靠的方法。而且,使用一般控制结构来判断和处理异常,需要明显增加程序长度并且使程序的逻辑变得繁琐难懂。有些程序设计语言提供了比较完善的异常处理机制,如Java语言。
9.独立编译
独立编译是指能分别编译各个程序单元,然后再把它们集成为一个完整的程序。一个大程序往往由许多不同的程序单元(如过程、函数、子程序或模块等)组成。如果每修改其中任何一个程序单元,都需要重新编译整个程序,那么程序开发、调试和维护的成本将会大大增加。如果该程序设计语言支持各个程序的独立编译,则只需要重新编译经过修改了的程序单元,然后重新连接整个程序即可。显然,一个具有独立编译机制的程序设计语言是比较适合于软件工程需要的。11.1.5编程语言的选择
当软件开发成为一种工程化生产,程序编码也不再是程序员个人的事,而需要许多程序员同心协力地投入才能胜任。在编码前必须确定程序设计的方法,把大的软件开发任务细化,然后每个程序员才能同时进行自己所要完成的工作。
不同的编程语言有着不同的优点,也有着它们不足的地方。目前在计算机上的程序设计语言越来越多,给程序员提供了更多选择的机会。开始时程序员一般选择自己熟悉的语言,但如果设计者能根据实际需要选择合适的程序语言,将使编码及代码维护更容易,测试工作量也会减少。由于软件系统的绝大部分成本用在生命周期的测试和维护阶段,所以易于测试和维护是选择程序设计语言的重要因素。总地来说,理想的程序设计语言应该具有良好的模块化机制,良好的控制结构、数据结构和数据安全性,较好的可读性、可移植性和可维护性,较完备的开发工具和开发环境,良好的编译调试系统和独立编译机制,等等。
具体来说,编程语言的选择一般应遵循以下几点标准。
1.语言的适用领域
不同的语言都有其自己的适用领域。在科学与工程计算领域方面,FORTRAN语言、Pascal等最为常用。在数据库、信息系统方面,SQL、Oracle、FoxPro都是适用的语言,而SQL较为常用。实时应用系统要求有很快的响应速度,此时可选用汇编语言、Ada语言或C语言等。在网络应用中,Java是较为合适的语言。总之,程序员应根据不同语言的特点,结合所要开发的软件,选择最为适合的语言。
2.软件的运行环境
最终软件运行的硬件及软件环境也是影响语言选择的因素,要确保软件能运行于所处的环境中。开发出来的软件系统必须具有可用性,这是用户的必然要求。计算机操作系统之间的不兼容,使其成为软件开发中必须考虑的一大因素。如果软件需要运行在Windows操作系统下,则最好选择适合在Windows配置和运行的开发工具;如果软件要运行在UNIX/Linux下,就应该选择适合在UNIX/Linux环境下使用的开发工具;而从软件的体系结构来说,如果软件运行在Web/Browser结构下,就需要选择ASP/JSP等脚本语言来编写动态网页。
3.系统用户的要求
如果所开发的系统是由用户自己来维护的,则应该选择用户较为熟悉的语言来编写程序。系统使用和维护的周期相对较长,因而这个要求具有一定的合理性。
4.算法和数据结构的复杂程度
科学计算、人工智能领域方面的问题算法比较复杂,数据库应用、数据处理方面的算法比较简单,在选择编程语言时要考虑某种语言是否能满足算法的复杂程度。
5.兼容性和移植性
不同机器上能执行的语言可能不同,程序员还应考虑硬件设备对语言的兼容性。同时如果将来要把开发软件移植的话,还要考虑编程语言的移植性问题,使开发软件能移植到不同的硬件环境下运行。
6.软件开发方法
有些编程语言依赖于开发的方法。如果是过程化程序开发方法,宜采用过程化程序设计语言;若是面向对象的开发方法,则应采用面向对象的程序设计语言。软件开发方法的选择应适合具体的应用。
7.软件系统开发的规模
若开发的系统规模较大,对于一些设计语言不能适用,那么就要找出一种能承担如此大的系统规模的编程语言。有时候设计并实现一种供特定类工程项目专用的程序设计语言,可能也会是一个很好的选择。
8.程序设计人员的技术水平
选择编程语言时,还要考虑程序设计人员的知识以及技术水平。如果程序员不能对某种语言熟练地掌握以及没有实践的经验,是不能很好地用这种语言编写好程序的。虽然对于有经验的程序员来说,学习一种新的程序设计语言并不难,但是要完全掌握一种新语言却需要一定的实践。已有的语言已经积累了大量的久经使用的程序,具有比较完整的资料、支撑软件和软件开发工具,而且有过类似项目的开发经验和成功先例,显然这些都可以大大降低软件开发的工作量和日常维护工作的困难程度。熟悉的语言,使开发人员对于项目的成功更加有信心。所以,选择一种程序员都熟悉的语言,对于软件的开发无疑是事半功倍的。11.1.6面向对象语言的优点
面向对象设计的结果既可以用面向对象语言实现,也可以用非面向对象语言实现。使用面向对象语言时,由于语言本身充分支持面向对象概念的实现,因此,编译程序可以自动把面向对象概念映射到目标程序中。使用非面向对象语言编写面向对象程序,则必须由程序员自己把面向对象概念映射到目标程序中。例如,C语言并不直接支持类或对象的概念,程序员只能在结构(struct)中定义变量和相应的函数(事实上,不能直接在结构中定义函数而是要利用指针间接定义)。所有非面向对象语言都不支持一般—特殊结构的实现,使用这类语言编程时要么完全回避继承的概念,要么在声明特殊化类时,把对一般化类的引用嵌套在它里面。到底应该选用面向对象语言还是非面向对象语言,关键并不在于语言功能的强弱。从原理上说,使用任何一种通用语言都可以实现面向对象概念。当然,使用面向对象语言实现面向对象概念,远比使用非面向对象语言方便。但是,方便性也并不是决定选择何种语言的关键因素。选择编程语言的关键因素是语言的一致的表式方法、可重用性及可维护性。从面向对象观点看来,能够更完整、更准确地表达问题域语义的面向对象语言的语法是非常重要的,因为这会带来下述几个重要优点。
1.一致的表示方法
从前面章节的讲述中可以知道,面向对象开发基于不随时间变化的、一致的表示方法。这种表示方法应该从问题域到面向对象分析(OOA),从OOA再到面向对象设计(OOD),最后从OOD到面向对象编程(OOP),始终稳定不变。一致的表示方法既有利于在软件开发过程中始终使用统一的概念,也有利于维护人员理解软件的各种配置成分。
2.可重用性
为了能带来可观的商业利益,必须在更广泛的范围中运用重用机制,而不是仅仅在程序设计这个层次上进行重用。因此,在OOA、OOD直到OOP中都显式地表示了问题域语义,其意义是十分深远的。随着时间的推移,软件开发组织既可能重用它在某个问题域内的OOA结果,也可能重用相应的OOD和OOP结果。
3.可维护性
尽管人们反复强调保持文档与源程序一致的必要性,但是,在实际工作中很难做到交付两类不同的文档,并使它们保持彼此完全一致。特别是考虑到进度、预算、能力和人员等限制因素时,做到两类文档完全一致几乎是不可能的。因此,维护人员最终面对的往往只有源程序本身。
让我们以ATM系统为例,说明在程序内部表达问题域语义对维护工作的意义。假设在维护该系统时没有合适的文档资料可供参阅,于是维护人员人工浏览程序或使用软件工具扫描程序,记下或打印出程序显式陈述的问题域语义。维护人员看到“ATM”、“账户”和“现金兑换卡”等,这对维护人员理解所要维护的软件将有很大帮助。因此,在选择编程语言时应该考虑的首要因素是,在供选择的语言中哪个语言能最好地表达问题域语义。一般来说,应该尽量选用面向对象语言来实现面向对象分析、设计的结果。
11.1.7面向对象语言的技术特点
面向对象语言的形成借鉴了历史上许多程序语言的特点,从中吸取了丰富的营养。当今的面向对象语言从20世纪50年代诞生的LISP语言中引进了动态联编的概念和交互式开发环境的思想,从60年代推出的SIMULA语言中引进了类的概念和继承机制。此外,它还受到70年代末期开发的Modula-2语言和Ada语言中数据抽象机制的影响。
20世纪80年代以来,面向对象语言像雨后春笋一样大量涌现,形成了两大类面向对象语言。一类是纯面向对象语言,如Smalltalk和Eiffel等语言;另一类是混合型面向对象语言,也就是在过程语言的基础上增加了面向对象机制的语言,如C++ 等语言。
一般来说,纯面向对象语言着重支持面向对象方法的研究和快速原型的实现,而混合型面向对象语言的目标则是提高运行速度和使传统程序员容易接受面向对象思想。成熟的面向对象语言通常都提供丰富的类库和强有力的开发环境。
下面介绍在选择面向对象语言时应该着重考察的一些技术特点。
1.支持类与对象概念的机制
所有面向对象语言都允许用户动态创建对象,并且可以用指针引用动态创建的对象。允许动态创建对象,就意味着系统必须处理内存管理问题,如果不及时释放不再需要的对象所占用的内存,动态存储分配就有可能耗尽内存。
有两种管理内存的方法:一种是由语言的运行机制自动管理内存,即提供自动回收“垃圾”的机制;另一种是由程序员编写释放内存的代码。自动管理内存不仅方便而且安全,但是必须采用先进的垃圾收集算法才能减少开销。某些面向对象的语言(如C++)允许程序员定义析构函数(destructor)。每当一个对象超出范围或被显式删除时,就自动调用析构函数。这种机制使得程序员能够方便地构造和唤醒释放内存的操作,却又不是垃圾收集机制。
2.实现整体—部分结构的机制
一般来说,有两种方法——指针和独立的关联对象可实现整体—部分结构。大多数现有的面向对象语言并不显式支持独立的关联对象。在这种情况下,使用指针是最容易的实现方法,通过增加内部指针可以方便地实现关联。
3.实现一般—特殊结构的机制
一般—特殊结构的机制既包括实现继承的机制也包括解决名字冲突的机制。所谓解决名字冲突,指的是处理在多个基类中可能出现的重名问题。这个问题仅在支持多重继承的语言中才会遇到。某些语言拒绝接受有名字冲突的程序,另一些语言提供了解决冲突的协议。不论使用何种语言,程序员都应该尽力避免出现名字冲突。
4.实现属性和服务的机制
对于实现属性的机制,应该着重考虑以下几个方面:支持实例连接的机制;属性的可见性控制;对属性值的约束。对于服务来说,主要应该考虑下列因素:支持消息连接(即表达对象交互关系)的机制;控制服务可见性的机制;动态联编。
所谓动态联编,是指应用系统在运行过程中执行一个特定服务时,选择(或联编)实现该服务的适当算法的能力。动态联编机制使得程序员在向对象发送消息时拥有较大的自由,在发送消息前,无须知道接受消息的对象当时属于哪个类。
5.类型检查
程序设计语言可以按照编译时进行类型检查的严格程度来分类。如果语言仅要求每个变量或属性隶属于一个对象,则是弱类型的;如果语法规定每个变量或属性必须准确地属于某个特定的类,则这样的语言是强类型的。面向对象语言在这方面差异很大,例如,Smalltalk实际上是一种无类型语言(所有变量都是未指定类的对象);C++和Eiffel则是强类型语言。混合型语言(如C++、Objective_C等)甚至允许属性值不是对象而是某种预定义的基本类型数据(如整数、浮点数等),以可以提高操作的效率。强类型语言主要有两个优点:一是有利于在编译时发现程序错误,二是增加了优化的可能性。通常使用强类型编译型语言开发软件产品,使用弱类型解释型语言快速开发原型。总地来说,强类型语言有助于提高软件的可靠性和运行效率。现代的程序语言理论支持强类型检查,大多数新语言都是强类型的。
6.类库
大多数面向对象语言都提供一个实用的类库。某些语言本身并没有规定提供什么样的类库,而是由实现这种语言的编译系统自行提供类库。因存在类库,许多软构件就不必由程序员重头编写了,这为实现软件重用带来了很大方便。
类库中往往包含实现通用数据结构(例如动态数组、表、队列、栈和树等)的类,通常把这些类称为包容类。在类库中还可以找到实现各种关联的类。
更完整的类库通常还提供独立于具体设备的接口类(例如输入/输出流)。此外,用于实现窗口系统的用户界面类也非常有用,它们构成了一个相对独立的图形库。
7.效率
许多人认为面向对象语言的主要缺点是效率低。产生这种印象的一个原因是,某些早期的面向对象语言是解释型的而不是编译型的。事实上,使用拥有完整类库的面向对象语言,有时能比使用非面向对象语言得到运行更快的代码,这是因为类库中提供了更高效的算法和更好的数据结构。例如,程序员已经无须编写实现哈希表或平衡树算法的代码,类库中已经提供了这类数据结构,而且算法先进,代码精巧可靠。
认为面向对象语言效率低的另一个理由,是说这种语言在运行时使用动态联编实现多态性,这似乎需要在运行时查找继承树,以定义给定操作的类。事实上,绝大多数面向对象语言都优化了这个查找过程,从而实现了高效率查找。只要在程序运行时始终保持类结构不变,就能在子类中存储各个操作的正确入口点,从而使得动态联编成为查找哈希表的高效过程,不会由于继承树深度加大或类中定义的操作数增加而降低效率。
8.持久保存对象
任何应用程序都对数据进行处理,如果希望数据能够不依赖于程序执行的生命期而长时间保存下来,则需要提供某种保存数据的方法。希望长期保存数据主要出于以下两个原因:
●为实现在不同程序之间传递数据,需要保存数据;
●为恢复被中断了的程序的运行,首先需要保存数据。一些面向对象语言(例如C++)没有提供直接存储对象的机制。这些语言的用户必须自己管理对象的输入/输出,或者购买面向对象的数据库管理系统。
另外一些面向对象语言(例如Smalltalk)把当前的执行状态完整地保存在磁盘上。还有一些面向对象语言提供了访问磁盘对象的输入/输出操作。
通过在类库中增加对象存储管理功能,可以在不改变语言定义或不增加关键字的情况下,就在开发环境中提供这种功能。然后,可以从“可存储的类”中派生出需要持久保存的对象,该对象自然继承了对象存储管理功能。这就是Eiffel语言采用的策略。
理想情况下,应该使程序设计语言语法与对象存储管理语法实现无缝集成。
9.参数化类
在实际的应用程序中,常常看到这样一些软件元素(即函数、类等软件成分),从它们的逻辑功能看,彼此是相同的,所不同的主要是处理的对象(数据)的类型不同。例如,对于一个向量(一维数组)类来说,不论是整型向量、浮点型向量,还是其他任何类型的向量,针对它的数据元素所进行的基本操作都是相同的(例如插入、删除、检索等)。当然,不同向量的数据元素的类型是不同的。如果程序语言提供一种能抽象出这类共性的机制,则对减少冗余和提高可重用性是大有好处的。所谓参数化类,就是使用一个或多个类型去参数化一个类的机制。有了这种机制,程序员就可以先定义一个参数化的类模板(即在类定义中包含以参数形式出现的一个或多个类型),然后把数据类型作为参数传递进来,从而把这个类模板应用在不同的应用程序中,或用在同一应用程序的不同部分。Eiffel语言中就有参数化类,C++ 语言也提供了类模板。
10.开发环境
软件工具和软件工程环境对软件生产率有很大影响。由于面向对象程序中的继承关系和动态联编等引入了特殊复杂性,面向对象语言所提供的软件工具或开发环境就显得尤其重要了。至少应该包括下列一些最基本的软件工具:编辑程序、编译程序或解释程序、浏览工具和调试器(debugger)等。
编译程序或解释程序是最基本、最重要的软件工具。编译与解释的差别主要是速度和效率不同。利用解释程序解释执行用户的源程序,虽然速度慢、效率低,但却可以更方便、更灵活地进行调试。编译型语言适用于开发正式的软件产品,优化工作做得好的编译程序能生成效率很高的目标代码。有些面向对象语言(例如Objective_C)除提供编译程序外,还提供一个解释工具,从而给用户带来很大方便。某些面向对象语言的编译程序先把用户源程序翻译成一种中间语言程序,然后再把中间语言程序翻译成目标代码。这样做可能会使得调试器不能理解原始的源程序。在评价调试器时,首先应该弄清楚它是针对原始的面向对象源程序,还是针对中间代码进行调试的。如果是针对中间代码进行调试的,则会给调试人员带来许多不便。此外,面向对象的调试器应该能够查看属性值和分析消息连接的后果。
在开发大型系统的时候,需要有系统构造工具和变动控制工具。因此应该考虑语言本身是否提供了这种工具,或者该语言能否与现有的这类工具很好地集成起来。经验表明,传统的系统构造工具(例如UNIX的Make)目前对许多应用系统来说都已经太原始了。
11.2编码规范
一个软件质量的好坏不仅跟程序设计的语言有关,还与程序设计的规范与风格有着紧密的关系。程序员应当掌握适当的编程技巧,形成统一的编程风格,建立良好的编程习惯。编程的规范与风格是指程序员在编程时应遵循的一套形式与规则,主要是让程序员和其他人方便容易地读懂程序,理解程序功能及作用。
在相当长一段时间,许多人认为,程序只是用来给机器执行的,只要程序逻辑正确,能被机器理解和执行就足够了。但随着软件规模日趋庞大和程序复杂性的增加,人们逐渐发现,在软件生命周期过程中,许多程序都需要被重新阅读,特别是被编写该程序以外的其他开发人员阅读。这在软件测试阶段和维护阶段表现得尤为明显。当阅读程序成为软件开发和维护过程中的一项重要工作的时候,可读性差的程序将花费开发人员更多的时间和精力,而成为耗费软件系统开发成本的重要因素之一。
具有良好风格的程序,不仅能让人们容易读懂,方便人们修改程序错误,而且对程序运行的效率影响不大。而程序设计的风格不能是随意的,为了让不同的程序员理解、交流程序,减少因不协调而引起的问题,应将程序设计的风格统一化。直截了当的逻辑表达式、通用的语言使用方式、相应的注释说明等是必不可少的。虽然不同的程序员有不同的风格,但让程序风格尽量保持统一和规范,是编程的重要目标之一。因此在建立和使用编码规则时应遵循以下几条原则:
(1)遵循开发流程,在设计的指导下进行代码编写。
(2)代码的编写以实现设计的功能和性能为目标,要求正确完成设计要求的功能,达到设计的性能。
(3)程序具有良好的程序结构,要提高程序的封装性,降低程序的耦合程度。
(4)程序可读性强,易于理解。
(5)方便调试和测试,可测试性好。
(6)易于使用和维护,具有良好的修改性、扩充性。
(7)可重用性强,移植性好。
(8)占用资源少,以低代价完成任务。
(9)在不降低程序的可读性的情况下,尽量提高代码的执行效率。程序设计的风格一般包含四个方面:源程序文档化、数据说明、表达式及语句结构、输入/输出。
11.2.1源程序文档化
软件是程序和文档的集合。如果认为程序也是一种文档,那么软件项目中的文档是项目开展的唯一凭据和线索。源程序的文档化是这样一种工作:它为源程序的编写定义了一组相关的规范,从而确保和提高源程序的可理解性与可维护性。源程序文档化的内容包括标识符的命名、注释的安排及程序的视觉组织等。
1.标识符
程序中对常量、变量、数组、函数等的命名都是标识符。如定义变量object为一个项目,表示次数用times,总量用total,定义一维数组用a[3]表示。标识符应用与它本身含义相当或一致的英文、符号表示,使其能够见其文知其意。标识符的定义不能太长也不适宜太短,以便于记忆和理解为宜。而且两个标识符是不应用同一名字命名的,以免引起不必要的错误和混淆。
各种编程语言和编程环境对于标识符的命名和使用都有一定的具体要求,可以参照相应的规范实例进行学习。
例如,在VC++ 里,一般遵循以下一些命名准则:
(1)使用能描述常量、变量或函数的意义或用途的词命名。
(2)所有的常量名都用大写字母的字符串表示,不同的词之间用下划线“_”分开。
(3)函数名中每个词的第一个字母应大写,函数名一般不使用下划线来分隔词。
(4)变量命名应遵循匈牙利表示法,变量名以表示变量类型的小写前缀开头,其余词的第一个字母应大写。
部分常用的数据类型对应的前缀如表11.1所示。表11.1匈牙利表示法常用前缀遵循以上规则,可以写出以下一些变量命名的示例:
pszName:表示一个字符数组类型的指针,是一个局部变量。
m_pObject:表示某一个类中的一个成员变量,该变量是一个指向Object的指针变量。
g_blsValid:表示一个布尔(BOOL)类型的全局变量,标识某操作是否有效。
类的命名必须以C开头,而且紧接着C的第一个字母要大写。如可以用CStudent表示学生类,用CDlgLogin表示登录对话框。
Java语言的命名习惯和C++ 有些相似,但也有一些区别。Java常量名和C++ 语言一样,都使用大写的字符串,如AAABBBCCC。Java包中的字母一律用小写,如aaabbbccc。类名、接口名应该使用名词,其中每个单词的首字母都要大写,形式如AaaBbbCcc。而它的变量名和类的方法名的命名要求相同,其中第一个单词小写,后面每个单词的首字母大写,如aaaBbbCcc。
2.注释
程序中的注释是程序员与程序读者之间通信的必不可少的手段。由程序员开发的程序,在交给维护人员维护的过程中,如果没有程序的注释说明,维护人员是很难理解程序的。所以要求在开发程序时给程序作相应的文档说明,这些说明是用人的自然语言来描述的。有了程序的注释,就能给日后程序的修改提供很大帮助。在一些正规的程序中,注释行占到整个源程序的1/3~1/2。注释不影响程序的执行。注释一般分为序言性注释和功能性注释。
序言性注释通常放在每个程序模块的开始,简单描述这部分模块的程序标题、功能和目的说明、数据描述、主要算法特征,还有接口说明、模块位置、开发简史等。对理解程序起着引导作用。其中,数据描述主要是描述程序中重要的变量及其用途、约束或限制条件;接口说明主要是说明该模块的调用形式、参数描述和子程序清单等接口信息;模块位置标明该模块在哪一个文件中或隶属于哪一个软件包;开发简历包括模块设计者、复审者、复审日期、修改日期等。
一个模块的序言性注释可以是以下形式:
模块名:该模块的名称,相当于模块的标识符。
功能及目的:模块主要实现的功能及目的。算法:关于该模块主要的和关键的算法过程的描述。
变量名:关于模块所使用的各种参数和变量的说明。
调用者:调用该模块的其他模块。
隶属于:模块程序代码所在的源程序文件或包等文件及路径信息。
作者:程序的编写者;日期:程序编写的日期。
审核:程序的审核者;日期:程序审核的日期。
修改:程序的修改者;日期:程序修改的日期。
下面是一个C语言程序函数前的序言性注释的例子。通过这些注释,函数的功能和调用方法一目了然。类的描述后面才是作者、版本等信息。其中see后面的信息可以作为此类的参考。事实上,Short类是从Number类继承而来的。since后面一般加版本信息,表示从哪一个版本开始,本程序就已经存在。
功能性注释嵌入到源程序当中,用以说明其后的语句或程序段的功能,即做的是什么工作。如果程序不是太难理解,一般无需解释怎么做,因为程序本身就是最好的说明。
例如:
//distance等于speed乘以time
distance=speed*time;
显然,这样的注释对于程序的理解毫无意义,它本身只是程序语句的重复。书写功能性注释需注意以下几点:
(1)用于描述一段程序,而不是每一行语句。
(2)注释与程序分开,适当使用缩进和空行等方法,使程序和注释容易区别。
(3)注释说明要准确,修改程序也应该修改注释。
另外,应该避免在一行代码或表达式中间插入注释,否则将会使程序的可读性变差。清晰、准确的标识符命名,合理的代码组织结构,也会提高程序的自注释性,从而减少不必要的注释。从前面的例子可知,应该防止不必要的重复注释信息,在代码的功能、意图层次上解释代码的目的、功能和采用的方法,提供代码以外的东西,以帮助读者理解代码。有良好的标识符定义和恰当的注释说明就能得到比较好的源程序内部的文档。有关设计的说明也可作为注释,嵌入源程序体内,以提高程序的可读性。
值得一提的是,在Java语言里,由注释符号“/**”和“*/”括起来的部分称为文档注释,文档注释可以用Java提供的javadoc编译命令方便地转化为HTML文档。许多软件开发工具和环境都为程序的文档化提供了必要的支持。
3.标准的书写格式
程序的编写除了要使用规范的标识符和注释说明外,还应该注意程序的书写格式。编写源程序时遵循统一的、标准的格式,有助于改善程序的可读性。
一个程序如果写得密密麻麻,层次不分,显然让人难以读懂。恰当使用缩进和空行,可以使源程序代码结构更加清晰、美观。不同的程序段使用空行隔开,可以使冗长的程序代码更加清晰易读。而程序的缩进则避免了所有的代码行都从某一列开始,层次不分。按照程序本身的逻辑关系,对源程序代码进行必要的组织和编排,使它整体上错落有序,层次分明,从感观上来说也更加易于阅读和理解。
例如,if_else语句可以写成以下形式:常用的结构编码规则一般包括下述几点:
(1)每一行只写一条语句。
(2)如果一条语句需要多行,则所有的后续行往里缩进。
(3)使用分层缩进的写法显示嵌套结构层次。
(4)适当使用空格或圆括号作隔离符。
(5)注释段与程序段以及不同的程序段之间插入空行。
缩进格式有时会导致程序语句延续到下一行,从而使得程序代码以至程序逻辑更难读懂,因而要对程序的嵌套层数进行必要的限制。而代码行的长度太长也可能影响阅读,通常需要进行必要的换行。换行一般应选择在较高级别处断开,在一个逗号或一个操作符前断开。清晰的缩进格式可以增加读者理解程序的信心。另一方面,不适当的、前后不一致的缩进格式会给程序的阅读者造成很大的混乱。但还有一种可能的结果就是,良好的代码结构使读者想当然地认为程序编制得不错,程序逻辑也一定没有问题。
许多程序编码工具都对程序代码的格式规范提供了良好的支持。例如,不正确的和不适当的缩进所带来的问题,可以很容易地用自动程序格式化编排程序解决。11.2.2数据说明
数据说明是程序编码中必不可少的一部分。虽然数据结构的组织及其复杂性在设计阶段已经确定了,但对数据的说明却是在编程阶段进行的。为了使数据说明更容易理解和维护,在编写程序时,应该注意数据说明的风格。
数据说明主要应注意以下一些方面:
(1)数据说明的次序。数据说明的次序应规范化。从原则上说,数据说明的次序可以是任意的,并不影响程序的编译执行。但规范化的数据说明无疑能使程序的阅读和理解更加容易。应该固定数据说明的先后次序,使数据的属性更易于查找,从而有利于程序的测试、调试和维护。例如,可以按常量说明、简单变量类型说明、数组说明、公共数据块说明、文件说明的顺序进行数据说明。在类型说明中,还可进一步要求按整型、实型、字符型、逻辑型的顺序进行说明。当一个语句说明多个变量名时,应将这些变量按字母的顺序进行排列。例如,如下语句:
intsize,length,width,cost,price,amount;
可以改写成:
intamount,cost,length,price,size,width;
直观来说,上述方法显然可以帮助开发人员迅速而准确地定位数据说明在程序中的位置,有助于提高程序代码阅读的效率。
(2)复杂数据结构说明。对于一个复杂的数据结构,在程序实现这个数据结构时应该有相应的注释说明其特点,使开发人员能清晰地了解其结构特征。对于用户自定义的数据类型,更应该在注释中作必要的补充说明。
11.2.3语句结构
软件的逻辑结构在软件设计阶段已经确定了,但单个程序语句的构造则是编码阶段的任务。程序表达式和语句的构造应力求简单、直接,不能因为片面追求效率而使语句复杂化。程序员应该以清晰明了的形式写好程序表达式和语句。一般情况下,一行内只写一条语句或一条表达式,适当采用移行格式,使程序的逻辑和功能更加明确,增强程序的可读性。使用基本的控制结构编写程序,包括顺序结构、条件选择结构和循环结构。注意goto语句的使用,避免不必要的转移和相互交叉纠缠,因为这样只能使程序更加复杂难懂,出错率高。尽量采用简单的语句,避免太多的循环嵌套和条件嵌套,可以用逻辑表达式来
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年人社部的劳动合同(三篇)
- 2025年九年级英语下册教学工作总结范例(二篇)
- 2025年中外来料加工、来件装配合同样本(2篇)
- 2025年代理权转让的合同(2篇)
- 2025年企业产品购销合同参考模板(三篇)
- 2025年九年级英语培优辅差总结样本(二篇)
- 人工智能居间服务合同范本
- 亲子餐厅装修施工合同样本
- 植生混凝土技术施工方案
- 木材加工居间合作协议
- 2025公司借款合同范本借款合同
- 闽教版(2020)小学信息技术三年级上册第2课《人工智能在身边》说课稿及反思
- 语文-百师联盟2025届高三一轮复习联考(五)试题和答案
- 地理-山东省潍坊市、临沂市2024-2025学年度2025届高三上学期期末质量检测试题和答案
- 正面上手发球技术 说课稿-2023-2024学年高一上学期体育与健康人教版必修第一册
- 佛山市普通高中2025届高三下学期一模考试数学试题含解析
- 人教 一年级 数学 下册 第6单元 100以内的加法和减法(一)《两位数加一位数(不进位)、整十数》课件
- 事故隐患排查治理情况月统计分析表
- 永磁直流(汽车)电机计算程序
- 国家电网招聘2025-企业文化复习试题含答案
- 2024年江西省高考物理试卷(含答案解析)
评论
0/150
提交评论