软件技术基础 第三版课后习题答案Word文档格式.docx
《软件技术基础 第三版课后习题答案Word文档格式.docx》由会员分享,可在线阅读,更多相关《软件技术基础 第三版课后习题答案Word文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
![软件技术基础 第三版课后习题答案Word文档格式.docx](https://file1.bdocx.com/fileroot1/2022-12/9/87877562-47f6-4a69-aa3d-649df04d8056/87877562-47f6-4a69-aa3d-649df04d80561.gif)
程序约定自上向下自左向右地执行,即顺序地执行语句(或表达式)。
但不仅限于此,计算机之所以能自动计算,它能通过判断将程序转到应该执行的地方。
人们就是通过巧妙地安排控制转移,使计算机实施算法。
最基本的程序控制语句
在汇编语言时代就有了Jump指令使执行跳转,对应的高级语言语句是无条件转移语句:
gotoloop;
其中loop是跳转到的语句的标号(数字或标识符),加上条件判断子句if(E)就是条件转移语句:
If(E)gotoloop;
其中E是条件(布尔)表达式,求值结果是‘真’、‘假’值。
如果为‘真’转移到标号为l的语句,为‘假’则按顺序执行下一条语句。
有了这两个语句再加上简单语句(赋值、调用、输入/出),就可以实现程序的任何执行控制。
最基本的程序控制有以下三种:
顺序执行
简单语句序列S=S1;
S2;
…;
Sn
选择执行
结构化程序的控制结构
早期的编程语言是语句级的.用简单准语句集合加goto构成复杂的程序控制。
然而显式地使用goto语句是极其危险的。
这是因为:
·
goto语句相互交织使设计出的程序控制结构成为不可分解的一个整体。
尽管算法设计精巧,但牵一发而动全身。
程序一大,修改很困难。
显式使用goto语句使得任何写错转移语句标号的小错误都会导致灭项之灾。
使程序控制逻辑是结构化的,显式使用goto语句,程序依然难于阅读。
出了错易于找出错误并修改
易阅读导致易扩充、修改,大程序易于分析。
程序控制结构清晰,是它用关键字控制程序块(语句组)。
任何控制转移不能进入这些控制块,除非入口。
块中转出也不能直接转到程序其他处,只能转到出口。
在出/入口增加检查语句就使得程序错误真正局部化。
程序块级(语句组)控制采用语句括号使程序逻辑与表示法结构完全一致。
可直接编程。
程序语言结构化以后,编程对流程图的依赖就很少了。
Nassi-Schneldermann提出了结构化流程图(主要取消流线及箭头),因没有直接用类似结构化编程语言的伪代码方便而没有流行起来。
结构化编程语言的其他控制结构
结构化程序只需三种基本结构重复和嵌套。
但为了方便编程,第三代语言派生出许多结构。
以下简略说明。
条件分支
无假块条件分支和嵌套if语句
if(E)thenSTendif
if(E1)thenS1elseifE2thenS2…elseSmendif
无假块条件语句是正规条件语句的简化,很常用。
嵌套语句在否定部分用关键字elseif,有的语言用elsif。
只有当所有条件均有‘假’时才执行else块。
逐个检查m个条件效率很低,有时似无必要。
case语句
也叫分情形语句。
根据条件变量Z的值单独执行S1,S2…,Sn,执行完Si自动跳到endcase(C语言例外,它要加上break才跳。
否则执行Si+1到Sn)。
循环结构
除了doSwhileE之外,还有do-until和for结构。
do-until语句形式是doSuntilE,基流程图。
先执行再判断,若E为‘真’不再循环。
正好和先判断再执行的do-while语句是相反的。
将它改成do-while结构十分容易。
条件取反,先执行一次S块。
for-do语句
以控制变量增减值或枚举集合值计数的循环。
for-do是do-while的另一种变体结构,其书写格式有多种变体。
do-while-do语句
其形式是doS1whileEdoS2enddo流程图。
把与条件无关的部分或影响E值计算的部分作无限循环。
E为‘真’才作S2,否则执行enddo后面的语句。
2.3.3数据类型
计算机中计算对象(不管是常量、变量)都是有类型的,不能把一个实数和逻辑‘真’值(true)相加。
指出数据类型的一个目的就是要避免这类错误的发生。
各语言转换函数(inttoreal或realtiont)不尽相同,有的是把类型名作为转换函数名,不兼容的类型,如整数和布尔一般不能转换,但C语言例外。
数组类型
变量代表单个数据值叫纯量变量。
如果代表多个(数组)或多种(记录)值就叫它结构型数据的变量。
先来说数组:
以下声明可将其连接
BigArray(0)=Names()//第1元素放整个数组
bigArray
(1)=ages()//第2元素也放整个数组
使用时用二维索引
BigArray(0,7)=Names(7)//=不是VB语言符号
BigArray(1,7)=Ages(7)
记录类型
引入记录数据类型:
相同或不同类型数据组成的结构叫记录.记录型记录了对象的属性信息,记录的各个组成部分,称为记录域,各个域的数据类型可以不相同。
下面给出用VB声明的一个例子:
TypePersonRecord
NameAsString
AgeAsInteger
SexAsString
TelnumberAsString
LableAsInteger
EndType
类型定义中列出了属性名和属性的数据类型。
记录类型变量的一组值(或一行值)称作元组(tuples),由属性变量的值组成。
每个属性变量指明了一个(取值)域.也叫字段。
有了这个类型就可以声明记录型变量。
该变量在运算中只能按域取值,域的表示法是变量后接符号“.”再接域(属性)名。
指针类型
指针类型是一种很重要的数据类型,但同时也是一种简单却不大好理解的数据类型。
在讲述指针之前,首先来看一个例子(采用C语言来描述):
IntI;
//定义一个整型变量I
I=1;
//i赋值为1
I=i+1;
//表达式计算
在上面的例子中,首先定义一个整数型变量,然后设该变量的值为1。
在第三行使用了一个赋值语句:
i=i+1。
现在要问,i是一个数学意义上的变量吗?
如果是的话,i=i+1这个等式显然不能成立!
所以,计算机语言中的变量并不是数学意义上的变量,它代表的只是计算机内存中的一个存储单元。
这个单元中存放的内容是变化的,所以把它叫做变量。
每个变量都有一个名字(标识符),对应为存储单元的地址,存储单元的内容为变量的值。
引用变量的值时直接把变量名放到用值的地方(赋值语句的两边)。
程序运行时按地址存取内容。
如果某个变量的值是内存中的地址.这个变量叫指针变量。
C语言中指针类型如同数组类型,以类型指明符‘*’表示。
它所指向的对象是有类型的。
Inti*p;
//声明i为整型,P为整型对象的指针
charc,*pCh;
//声明c为字符型,pCh为指向字符里对象指针
‘*’放在变量标识符之前,出现在类型标识符之后,指明它所修饰的标识符是指向该类型对象的指针.p是指向整型的指针,*p是p所指向的无名变量的代名词.声明完成后变量p有存储单元但无内容.
同样,类型说明符‘&
’说明它所修饰的标识符为引用类型的变量。
int1.&
r=i;
2.3.4过程
例如要三次求正弦值,不是连续写三个求正弦值的程序,而是把这段程序从主程序分离开来,简称过程,可以多次调用。
分离出来的部分叫子例程(routine),执行完后依然返回原处。
带返回位的叫函数过程,不带返回值的叫子例程过程.简称子程序。
函数的自变量(参数)为x,并指出其函数(返回)值是Double类型.每当程序中出现求正弦时,直接调用该过程,称函数引用,即引用该函数的(返回)值。
一个主程序中写三段相似代码的执行情况是一样的,只是省写了两次,多了三次实参和形参匹配(置换)的执行过程。
函数过程是参数化(更抽象)的程序。
使用过程还可以降低程序复杂性、使程序结构变得消晰明了。
假设有一个主程序引用了30个函数,每个函数引用了100次,如果在每个引用函数的地方都要写出函数的代码的话,程序将变得十分繁杂。
如果使用函数,就显得简明清晰。
还可以提高编程效率。
许多可以标准化的函数,可以事先编写调试好,放到函数库中,使用时直接调用即可。
过程的定义
从过程关键字开始到过程结束之间的一段封闭的程序就是过程定义,它由型构(signature)和过程体(body)组成,以下是两种过程定义的结构:
FunctionFname(形式参数表)AS返回类型‘本行为函数型构’
[类型和数据声明]‘以下是函数体’语句集
EndFunc
sub名字《形式参数表》‘本行是子程序型构’
[类型和数据声明]‘以下是子程序体语句集
EndSub
以上可以看到除了关键字和形参表外过程和一般的程序没什么两样。
函数过程型构只比子程序型构多一个返回类型。
有的语言规定函数名必须在函数体中至少在赋值号左边出现一次,以便带回返回值,如VB。
有的语言可指定任何返回表达式,如C。
主程序中的数据通过型构的形参表进入过程。
在过程中也可以声明数据,不过这类数据与主程序没什么关系,主程序中无法访问它们,它们是局部变量。
局部变量的有效范围,即作用域(scope)仅限于本过程。
从主程序一侧看,这些局部变量都被过程隐藏起来了,只有型构中的参数表是通向外部的窗口(在此交换数据),所以型构也叫接口(Interface).为与一般变量区别把形参表中的变量称为变元(Argument)。
请看下面的两个例子
过程调用
函数过程因返回位可以出现在主程序的表达式中,以函数名引用函数值。
并列出与形参表变元的个数、类型、次序一样的实在参数表.如
Fahrenheit=Degress(42.0)//函数引用作为表达式
子程序过程的使用是过程调用,它相当于浓缩的一段程序。
PrintNStar5//调用过程,打印五个星号
过程调用是把过程体的代码调回到主程序的环境下执行。
形参和实参变元匹配之后过程运行,和主程序中其他代码运行没什么两样.主程序中声明的变量在过程中自动可用。
全程变量和局部变量,局部变量在过程执行完之后所有的数据和过程体都消失了,因此出了过程体再引用过程中的变量当然出错。
过程调用这种执行机制,为程序运行、节省内存空间带来了极大的好处。
程序中的数据对象,自它声明时起到本程序块结束(见到End)为其有效的作用范围(作用域)。
显然,内块嵌入在外块内,外块的变量,内块中是能见到的,这就可能导致这样一个问题:
外块的变量与内块同名怎么办?
程序设计语言中以“就近声明优先”准则来处理。
内块程序中所有出现X的地方均按布尔类型解释,Double型的X虽在其作用域内但被覆盖(overrided)了,通过点表示法才能出现在内块中:
(P.X>
5.0)andX
指明头一个X是P中声明的X,是Double类型的。
后一个X是本块Boolean类型的。
2.3.5过程的数据传递
过程是一段封闭的程序,型构是它与外界通信的接口,它的变量是抽象的,通过形、实参数匹配具体执行。
执行出口是本过程末端(除非中间有显式的retun语句).数据出入有两个途径,一为全程量,一为参数表。
上小节己说过全程量,此处讨论参数表。
无参过程
2.3.6变量的生命期和Static变量
程序中变量生命期随其声明所在程序块而异。
程序一执行完它的所有数据变量均失去意义,它所在的存储区则可另行分配其他程序装入执行。
因此,主程序中变量的生命期比过程中变量生命期要长,内嵌的子过程其变量生命期更短,最长的是文件变量,它放入磁盘,程序没有了它依然存在,所以按“生命期”的长短有:
持久变量除非人为销毁
全局变量出了程序便失去定义
静态局部变量见下文
自动变量(局部变量)出了所在块便失去定义
循环控制变量出了循环便失去定义
常常有一种需要,一个局部变量在它所在的局部程序块消失后依然保留其值,又不希望它是全局变量被该局部块以外的程序引用。
例如伪随机数发生器,为使伪随机数每次调用新值,则上次数是生成下次数的种子,如不希望其他操作影响这个发生器,此时用Static(静态)变量更方便:
2.3.7输入/输出
程序总得把计算的结果告诉使用者,输出一个可识别的结果。
非简单计算情况下还得在运行时获得用户的输入。
程序的输入/输出分作两大类:
一类是程序之间以文件形式进行数据传递;
另一类是人一机交互,把人们可识别的形式(字符串、数)按一定格式输入到程序变量中。
输出则相反,按用户要求的格式显示或打印。
这一般由高级程序设计语言以过程调用(标准过程)的形式实现。
过程在高级语言内部通过调用操作系统的系统调用完成。
输出:
DF,CF=__35.2272410___0.0000000
C语言的输出语句功能很强且具有计算机功能;
输出变量处直接写表达式,可以输出其结果值。
例如:
Printf(“DF,CF=%12.7f%12.7f\n”,a+b*df,3.0/7.0)
在VB中,可以利用MsgBox函数创建一消息对话框,向用户反馈程序的输出信息。
2.4面向对象程序设计语言的基本特征
2.4.1对象概述
如图2.15所示,设项有一个程序有100个子程序,经过分析,这100个子程序并不是每个子程序都要用到所有的数据,把数据相关和程序相关(有嵌套调用)的分成组。
例如,可以看到Sub1~Sub22加工第一组数据、Sub23~Sub57加工第2组数据……
一个大程序分成五个大模块,只留过程接口等待外面调用。
当然,模块间也可以彼此调用,例如图2.16中,Sub22调用Sub89、Sub43调用Sub62等。
尽管数据可沿着调用路线来回串,但加工最后结果都落实在各模块声明的数据存储单元内,总结果若在第二、四模块内,下一道打印命令(也是调用),它就把自己那组数打印出来。
进一步分析发现这些大模块的数据和操作往往是描述客观世界中的一个对象,例如一个堆栈、一台打印机、一个雇员、一个窗口……拿数据堆栈来说,堆栈体(由数组或队列类型实现)、入栈的数据和栈高指示(变量)就是堆栈的数据,压入(Push)、弹出(Pop)就是它的基本操作,询问是否空(IsEmpty)是否满(IsFull)是它的辅助操作。
这样封装的程序块就是一个复杂的计算对象,私有的数据描述了本对象的状态(如数据堆栈的情况);
操作表示了本对象的行为(能接受询问IsEmpty、IsFull,会压栈Push,会弹栈Pop):
对象接受外界的消息而动作,其结果是改变了对象内部的状态(数据在栈中出入)。
请参看图2.18所示。
这样封装之后使用者就不必关心对象内部情况,只按接口规定的形式向它发消息(如同调用)就可以了。
如对象的设计者发现某个操作的算法(过程体)不好,重新改写一个,使用者也不用知道,只要接口形式不变就行。
过去只有数据类型及数据结构、过程(算法描述)和嵌套过程,把一个活生生的世界硬拆成过程式程序表达,使用者要知道许多内部细节,设计者调试起来也极不方便。
程序对象提供了直接描述客观世界对象的有力手段.
数据叫做对象的属性(Attribute);
操作则改称方法(Method),即改变属性的方法。
对象间相互只有通信,调用方法叫发消息(Message)。
消息——方法与过程调用——过程体的定义几乎完全一样,但意义不同。
过程式语言在过程调用时主程序等待直至过程返回:
消息则不一样,因对象是自主的程序实体,发消息者可等可不等,接受消息的对象可以立即响应可以稍晚些时间响应,这降低了对象间的引用耦合,为并发程序、事件驱动程序提供了程序实现的技术基础。
2.4.2类与对象
对象是封装了属性和方法的实体,客观世界的对象往往有许多相似之处,例如,一个班上40个人,就学生而言他们的属性和方法完全一样,只是姓名、年龄不一样,交作业的内容不一样。
沿用程序中表达数据的抽象办法:
定义一个类型、声明该类型的变量,定义一类对象然后声明它的不同实例(Instance):
DimvAsDouble//v是双精型的数据变量
DimOKAsCommandButton//OK是按钮类的对象变量
这个类CommandButton是系统事先定义好的,OK是它的实例对象。
OK有着和CommandButton一样的属性和方法,只是要给出各属性的值。
类是生成实例的样板,是实例加工厂(给出一组属性值就是一实例)。
正是因为变量一类型和实例一类的相似性,许多语言(如C++)都把类看作是类型,类的定义如同以简单的类型构造复杂的数据类型一样,只不过类定义时还要定义类的方法。
类方法可以只写型构.方法体可以在类之外定义,以下是C++的字符堆栈类:
这个程序的图示如图2.18。
C++的main()函数相当于实例对象,Java已将它改成主程序对象的方法(外面套上对象名)。
当声明:
Char_stackstk1(100);
消息和方法
消息(message)相当于过程语言的过程调用,可带实在参数:
方法(method)则相当于过程定义,带参数也是形式参数,一定要有方法体(执行语句集).面向对象中只有消息一方法,没有过程调用一过程体的说法。
方法体中又可以向其他对象发消息,类似于本例的main向char_stack、stkl、stk2发消息。
这样一个应用程序就是多个对象之间发消息。
类对象接受生成、撤消实例对象的消息,这些方法叫类方法。
相应用到的变量叫类变量。
类定义中的其他方法和数据变量叫实例方法和实例变量。
实例变量如上所述,每生成一个实例对象就要复制一份。
实例方法只有一份,所以面向对象程序运行前要先装入类。
类与类型
类是由简单类型组成的复杂类型,和用户定义的复杂类型即学术上叫抽象数据类型(ADT)有相似之处:
类型名封装数据和操作
数据由若干基本类型或已实现的复杂类型定义的变量组成
操作是加工这些数据的子例程和函数
有外部可见性控制public(公有)、Private(私有)
和类型不同之处:
类型定义实例变量参与程序运行,类型仅是对变量的计算性态的描述.不参与运行。
类也是程序对象参与程序运行。
子类型是类型的真子集,不是操作减少就是数据取值范围缩小,子类是类的例化.它增加数据和操作,使对象更明确。
从复杂数据类型到类是一个飞跃,虽然底层的计算仍然是基本数据类型和子例程,但换了个说法,把类型数据叫属性集,子例程叫方法,过程调用叫发消息,就便面向对象程序和过程大不相同。
过程式程序的执行顺序,尽管是并发程序,都是设计者事先考虑好了的执行过程。
面向对象程序由于对象相对独立可以支持事件驱动程序。
一个窗口上有按钮、菜单、图标、图符,用户可以任意点击一个都能运行,其先后没有约定,用过程式语言就要编一个轮循响应程序,依次问用户点击了谁,如果增加了按钮或删除某个图符,轮循程序就要改,否则无法运行。
面向对象程序天然支持这类应用其示意图如图2.19所示:
对象A、B、C都是独立的,联线是表示运行时发消息的路径,不运行是没有的。
空心箭头是触发的消息(例如鼠标点击),图示给出两个,谁先谁后都没有关系,增加或减少一个对象或方法,只对用到它的对象稍作修改,不需要更改总控程序。
2.4.3类定义
面向对象编程,主要学会定义类。
VB的所谓面向对象编程是不彻底的,它的控件都是对象,但其类定义由系统做,用户只能在它提供的属性和方法的前提下.生成并使用实例对象,用户全无类定义概念。
VB.Net回到面向对象语言的大家庭,才提供类定义机制,所以本节只能用C++举例。
类定义以类名封装类成员,其中分数据成员和方法成员。
只有指明它们是公有的(Public),其他类的对象才可以访问,私有(Private)成员类内成员均可访问。
数据和操作全部公有失去封装和数据隐藏的意义.全部私有只是一个孤立的对象,也失去对象通信模拟客观世界对象的意义.所以,一般是数据成员全部私有,方法成员多半公有、少量私有。
私有方法只接受公有方法的消息,间接为类外对象服务.2.4.2节char_stack类的例子就是很好的类定义,该定义中没有私有方法。
构造子和析构子
类是对象的制造厂,生成的实例对象在运行前必须初始化,这个工作如果在定义数据成员时都加上初始化属性值,生成的对象雷同,就没有必要定义类了。
所以每个类都定义一个构造实例的方法叫构造子(constructor.C++译为构造函数,因为它的操作只有函数),用户设定不同的参数,就可以构造出不同的实例,在2.4.2节例的main()中stkl是可装100个字符的堆栈,stk2可装20个字符的堆栈。
构造子一般与类同名,在声明实例对象时也就等于在给类对象发消息。
同名的构造子可以定义多个,这样,构造实例的方法多样编程就很方便。
比如,可以定义一个不填参数的方法(无参),当用户不关心堆栈大小时,无参方法就是缺省的,由系统给定10个或其他数目。
再如,若根据程序运行情况动态地生成实例时,其构造子也不同。
方法的名字相同,所带参数不同,即方法体不同,这叫重载(overloading),即一个名字代表了好几个方法。
编译或运行时根据参数的类型、个数、次序匹配。
析构子(destructor)和构造子相反,当程序不再需要该实例对象时,及时撤销以释放它所占据的空间。
析构子的定义是在类名前加‘~’号,不带参数,在main()中写以下语句:
stk2.~char_stack();
则撤销stk2对象。
接口类有什么用呢?
用处很大!
因为同一接口类可以由不同的类实现,而应用程序关心的是接口提供的行为而不关心实现的细节。
例如字符堆栈,一个类用数组作栈体,一个类用链表,都可以实现堆栈操作,具有极大的灵活性。
更