1
 软件工程
1.7.1.1 5.1.1 程序设计语言

5.1.1 程序设计语言

编码的目的是实现人和计算机的通信,指挥计算机按人的意志正确工作。程序设计语言是人和计算机通信的最基本工具,程序设计语言的特性不可避免地会影响人的思维和解决问题的方式,会影响人和计算机通信的方式和质量,也会影响其他人阅读和理解程序的难易程度。因此,编码之前的一项重要工作就是选择一种恰当的程序设计语言。本节将从软件工程的观点,简单讨论几个和程序设计语言有关的问题。

1.程序设计语言分类

现有的程序设计语言虽然种类繁多,但它们基本上可以分为汇编语言和高级语言(包括超高级语言)两大类。

汇编语言的语句和计算机的硬件操作有一一对应关系,每种汇编语言都是支持这种语言的计算机所独有的,因此,基本上有多少种计算机也就有多少种汇编语言。高级语言使用的概念和符号与人们通常使用的概念和符号比较接近,它的一个语句往往对应若干条机器指令。一般来说,高级语言的特性不依赖于实现这种语言的计算机。

对于高级语言还可以进一步分类,以加深对它们的了解。我们可以分别从语言应用特点、语言内在特点及高级语言发展历程三个不同角度对高级语言进行分类。

1)从语言的应用特点分类

从应用特点看,高级语言可以分为基础语言、结构化语言和专用语言三类。

(1)基础语言是通用语言,它们的特点是历史悠久、应用广泛,有大量软件库,为最广泛的人所熟悉和接受。这类语言包括Basic、Fortran、Cobol和Algol。

(2)结构化语言是通用语言。这类语言的特点是直接提供结构化的控制结构,具有很强的过程能力和数据结构能力。Algol是最早的结构化语言,由它派生出来的PL/1、Pascal、C 及Ada等语言正广泛应用于各个领域。

(3)专用语言。其特点是:具有为某种特殊应用而设计的独特的语法形式。一般来说,这类语言的应用范围比较狭窄。例如,APL是为数组和向量运算设计的简洁且功能很强的语言,然而它几乎不提供结构化的控制结构和数据类型;Bliss是为开发编译程序和操作系统而设计的语言;Forth是为开发微处理机而设计的语言,它的特点是以面向堆栈的方式执行用户定义的函数,因此能提高速度和节省存储;Lisp和Prolog两种语言特别适合于人工智能领域的应用。

2)从语言的内在特点分类

从语言的内在特点看,高级语言可以分为系统实现语言、静态高级语言、块结构高级语言和动态高级语言等四类。

(1)系统实现语言是为了克服汇编程序设计的困难而从汇编语言发展起来的。这类语言提供控制语句和变量类型检验等功能,但是同时也容许程序员直接使用机器操作。例如,C语言就是著名的系统实现语言。

(2)静态高级语言给程序员提供某些控制语句和变量说明的机制,但是程序员不能直接控制由编译程序生成的机器操作。这类语言的特点是静态地分配存储。这种存储分配方法虽然方便了编译程序的设计和实现,但是对使用这类语言的程序员增加了较多的限制。因为这类语言是第一批出现的高级语言,所以使用非常广泛。Cobol和Fortran是这类语言中最著名的代表。

(3)块结构高级语言的特点是提供有限形式的动态存储分配,这种形式称为块结构。存储管理系统支持程序的运行,每当进入或退出程序块时,存储管理系统分配存储或释放存储。程序块是程序中界限分明的区域,每当进入一个程序块时就中断程序的执行,以便分配存储。Algol和Pascal是这类语言的代表。

(4)动态高级语言的特点是动态地完成所有存储管理,也就是说,执行个别语句可能引起重新分配存储或释放存储。一般来说,这类语言的结构和静态的或块结构的高级语言的结构都很不相同。这类语言一般是为特殊应用而设计的,不属于通用语言。

3)从程序设计的发展历程分类

从软件工程的角度,根据程序设计语言的发展历程,可以把它们大致分为2类。

(1)高级程序设计语言(第三代语言)。传统的高级程序设计语言包括上述提到的Fortran、Cobol、Algol,Basic语言。目前,它们都已经有多种版本,有的语言得到较大的改进,甚至形成了可视的开发环境,具有图形设计工具、结构化的事件驱动编程模式、开放的环境,使用者可以快速直观地编写各种应用程序。

(2)4GL(第四代语言)。4GL用不同的文法表示程序结构和数据结构,但是它是在更高一级抽象的层次上表示这些结构,它不再需要规定算法的细节。4GL兼有过程性和非过程性的两重特性。程序员规定条件和相应的动作是过程性的部分,并且指出想要的结果,这是非过程的部分。4GL又分为以下几种类型。

①查询语言:用户可利用查询语言对预先定义在数据库中的信息进行较为复杂的操作。

②程序生成器:只需很少的语句就能生成完整的第三代语言程序,不必依赖预先定义的数据库。

③其他4GL:如判定支持语言、原型语言、形式化规格说明语言等。

2.程序设计语言的特点

1)名字说明

预先说明程序中使用对象的名字,使编译程序能检查程序中出现的名字的合法性,从而能帮助程序员发现和改正程序中的错误。某些语言(Fortran和Basic)并不要求用户明示程序中所有对象的名字,第一次使用一个名字被看做是对这个名字的说明。然而在输入源程序时如果拼错了名字,特别是如果输错了字符,那么因此造成的错误是较难以诊断的。

2)类型说明

类型说明和名字说明是紧密联系的,通过类型说明,用户定义了对象的类型,从而确定了该对象的使用方式。编译程序能够发现程序中对某个特定类型的对象使用不当的错误,因此有助于减少程序错误。

3)初始化

程序设计中最常见的错误之一是在使用变量之前没有对变量或对象初始化。为减少发生错误的可能性,应该强迫程序员对程序中说明的所有变量初始化。另一个办法是在说明变量时由系统给变量赋一个特殊的表明其未被初始化的值,以后如果没给这个变量赋值就企图使用它的值,系统就会发出出错信号。

4)程序对象的局部性

程序设计的一般原理是,程序对象的名字应该在靠近使用它们的地方引入,并且应该只有程序中真正需要它们的那些部分才能进行访问。这些也就是局部化和信息隐蔽的原理。通常有两种提供局部变量的途径,Fortran和绝大多数系统实现语言提供单层局部性,块结构语言提供多层局部性。

如果名字的特性在靠近使用这些名字的地方说明,程序的读者就很容易获得有关这些名字的信息,因此多层次的局部性有助于提高程序的可读性。此外,具有多层局部性的语言鼓励程序员尽量使用局部变量和常量,这不仅有助于提高程序可读性,而且有助于减少差错和提高程序的可修改性。

5)程序模块

块结构语言提供了控制程序对象名字可见性的某些手段,主要是在较内层程序块中说明的名字不能被较外层的程序块访问。此外,由于动态存储分配的缘故,在两次调用一个程序的间隔中不能保存局部对象的值。因此,即使是只有一两个子程序使用的对象,如果需要在两次调用这些子程序的过程中保存这个对象的值,也必须把这个对象说明成全局的,然而这将增加维护时发生错误的可能性。

6)循环控制结构

最常见的循环控制结构有For语句、While语句等。但是,实际上有许多场合需要在循环体内任意一点测试循环结束条件,如果使用If语句和附加的条件表达式实现这个要求,则将增加程序长度并降低可读性。某些程序设计语言考虑到上述要求,并且适当地解决了这个问题,例如,Ada语言提供了Exit语句。

7)分支控制结构

单分支和双分支结构的If语句,通常并不存在实际问题,但是多分支的Case语句却可能存在下述两个问题:如果Case表达式的取值不在预先指定的范围内,则不能决定应该做的动作;在某些语言中,由Case表达式选定执行的语句,取决于所有可能执行的语句的排列次序,如果语句次序排错了,编译和运行时系统并不能发现这类错误。

8)异常处理

程序运行过程中发生的错误或意外事件称为异常。多数程序设计语言在检测和处理异常方面几乎没给程序员提供任何帮助,程序员只能使用语言提供的一般控制结构检测异常,并在发生异常时把控制转移到处理异常的程序段。但是,当程序中包含一系列子程序的嵌套调用时,并没有方便而又可靠的方法把出现的异常信息从一个子程序转送到另外的子程序。使用一般控制结构加布尔变量的方法,需要明显增加程序长度,并且使程序的逻辑变得烦琐难懂。

9)独立编译

独立编译意味着能分别编译各个程序单元,然后再把它们集成一个完整的程序。典型地,一个大程序由许多不同的程序单元(过程、函数、子程序或模块)组成,如果修改了其中任何一个程序单元都需要重新编译整个程序,这将大大增加程序开发、调试和维护的成本;反之,如果可以独立编译,则只需重新编译、修改的程序单元,然后重新连接各个程序即可。由此可见,一个程序设计语言如果没有独立编译的机制,就不是适合软件工程需要的好语言。

3.选择一种语言

开发软件系统时必要做出的一个重要抉择是,使用什么样的程序设计语言实现这个系统。适宜的程序设计语言能根据设计去完成编码,可以减少程序测试量,并且可以得出更容易阅读和维护的程序。由于软件系统绝大部分成本用于生存周期的测试和维护阶段,所以容易测试和维护是极其重要的。

一般来说,高级语言明显优于汇编语言,因此,除了在很特殊的应用领域(例如,对程序执行时间和使用空间有严格限制;需要产生任意的甚至非法的指令序列;体系结构特殊的处理机,以致在这类处理机上通常不能使用高级语言),或者大型系统中执行时间要求非常严格的(或直接依赖硬件的)一小部分代码需要用汇编语言书写之外,其他程序应该一律用高级语言书写。在选择高级语言编写软件时,有下述几条重要的使用标准。

(1)系统用户的要求。如果所开发的系统由用户负责维护,用户通常要求用他们熟悉的语言编写程序。

(2)可以使用的编译程序。运行目标系统的环境中可以提供的编译程序往往限制了可以选用的语言范围。

(3)可以得到的软件工具。如果某种语言有支持程序开发的软件工具可以利用,则目标系统的实现和验证都变得比较容易。

(4)工程规模。如果工程规模很庞大,现有的语言又不完全适用,那么设计并实现一种供这个工程项目专用的程序设计语言,可能是一个正确的选择。

(5)程序员的知识。虽然有经验的程序员学习一种新的语言并不困难,但是要完全掌握一种新语言却需要实践。如果和其他标准不矛盾,那么应该选择一种已经为程序员所熟悉的语言。

(6)软件可移植性要求。如果目标系统将在几台不同的计算机上运行,或者预期的使用寿命很长,那么选择一种标准化程度高、程序可移植性好的语言就很重要。

(7)软件的应用领域。所有的通用程序设计语言实际上并不是对所有的应用领域都同样适用的,例如,Fortran语言特别适合于工程和科学计算,C语言和Ada语言适用于系统和实时应用领域,Lisp语言适用于组合问题领域。选择语言时应充分考虑系统的应用范围。