《C程序的流程设计.ppt》由会员分享,可在线阅读,更多相关《C程序的流程设计.ppt(151页珍藏版)》请在三一办公上搜索。
1、第三章 C程序的流程控制,3.1 算法,通过前两章的学习,不少读者可能已经悟出,计算机尽管可以完成许多极其复杂的工作,但实质上这些工作都是按照人事先编好的程序的规定进行的。所以人们常把程序称为计算机的灵魂。随着计算机应用的日益广泛和深入,人们在研究和开发新的软、硬件技术的同时,也对程序本身和程序设计方法进行了深入的探讨。1976年瑞士计算机科学家Niklaus Wirth在他的惊世之作中提出了一个著名的公式:算法+数据结构=程序,图3.1 Niklaus Wirth,他认为,“程序就是在数据的某些特定表示方式和结构的基础上对抽象算法的具体表述”。Wirth企图用这个公式来对程序进行一个概括性的
2、定义。从今天的观点来看,它只能是对过程化程序的一个抽象定义,对面向对象的程序而言则不尽然。不过对学习C语言这样的面向过程的程序设计语言而言,是完全适用的。也就是说,面向过程的程序有两大要素:算法和数据结构。数据结构是程序所处理的对象数据的表示和组织形式。数据类型就是其重要内容。关于数据结构的概念在学习完第5、6、7章后,才会有较深的体验。,3.1.1 算法的组成要素与基本性质 算法含有两大要素:(1)操作算法是由一系列操作组成的。每个操作的确定不仅取决于问题的需求,还取决于它们取自哪个操作集,它与使用的工具系统有关。如算盘上的操作集由进、退、上、下、去等组成;做菜的操作集包括坐锅、加油、炒、煮
3、、炸、蒸、焖、加水、加调料等;驾驶汽车的操作包括踩离合器、踩油门、开电门、换档、左转、右转、开灯、关灯等。计算机算法要由计算机实现,组成它的操作集是计算机所能进行的操作。而且这些操作的描述与程序设计语言的级别有关。在高级语言中所描述的操作主要包括:算术运算(+、-、*、/)、逻辑运算(“与”、“或”、“非”等)、关系运算(=、=、=、!=等)、函数运算、位运算、I/O操作等。计算机算法是由这些操作所组成的。,(2)控制结构算法的另一要素是其控制结构。每一个算法都要由一系列的操作组成。同一操作序列,按不同的顺序执行,就会得出不同的结果。控制结构即如何控制组成算法的各操作的执行顺序。结构化程序设计
4、方法要求:一个程序只能由三种基本控制结构(或由它们派生出来的结构)组成。1966年Bohm和Jacopini证明,由这三种基本结构可以组成任何结构的算法,解决任何问题。这三种基本结构是:,(1)顺序结构。顺序结构中的语句是按书写的顺序执行的,即语句的执行顺序与书写顺序一致。这种结构像一串珍珠项链一样清晰可读。这是一种理想的结构,但是光有这样的结构不可能处理复杂的问题。一般说来,程序中的语句是顺序执行的。但是,顺序执行的程序的功能是非常有限的。为了提高程序的灵活性,必须采用不同的程序流程结构。(2)选择结构。最基本的选择结构是当程序执行到某一语句时,要进行一下判断,从两种路径中选择一条。例如,要
5、在两个数a,b中取一个最大的数就要经过比较判断,决定是将a还是将b输出。选择结构给程序注入最简单的智能。由二分支选择,可以派生出多分支选择结构。(3)循环结构(或称重复结构)。这种结构是将一条或多条语句重复地执行若干遍。就像驴子拉磨一样,虽然每一圈的操作都比较简单,而且相同,但磨上若干圈后就能把麦子磨成面粉。众所周知,电子计算机的一大优势是速度快。当能把一个复杂问题用循环结构来实现时,就能充分地发挥计算机的高速度的优势。,2.算法的基本性质 简单地说,算法就是进行操作的方法和操作步骤。例如,菜谱实际上是做菜肴的算法,乐谱实际上是演奏的算法,计算机程序是用某种程序设计语言描述的解题算法。通常认为
6、算法有如下一些性质:(1)有效性 有效性指算法所规定的操作都应当是能够有效执行的。例如,对于操作汽车的算法,有效的操作就是加速、刹车、换档、转动方向盘、鸣笛等,要让汽车执行“跳起”就是无效的操作。同样,一个计算机算法必须是计算机能够执行的。(2)确定性 确定性具有两重意义:一是所描述的操作应当具有明确的意义,不应当有歧义性。例如,不能发出这样的操作指令:“执行一个算术操作”。因为它既没有指出算术操作的类型,也没有指出操作数。确定性的另一重意义:操作作序列只有一个初始动作,序列中每一动作仅有一个后继动作;序列终止表示问题得到解答或问题没有解答,不能没有任何结论。(3)有穷性 有穷性指算法所规定的
7、操作序列必须在允许的时间内结束。例如,一个计算机算法要执行100年以上,就失去有穷性。,3.1.2 算法描述工具 为了描述算法,人们创建了许多算法描述工具。下面介绍程序设计中常用的几种方法,并主要介绍如何用这些工具描述算法的三种基本结构。1.流程图 流程图是一种流传很广的算法描述工具。这种工具的特点是用一些图框表示各种类型的操作,用线表示这些操作的执行顺序。我国国家标准GB 152689中推荐的一套流程图标准化符号,它与国际标准化组织ISO(International Standard Organization)提出的ISO流程图符号是一致的。图3.2为其中常用的一些符号。,图3.2 常用的流
8、程图标准化符号,2.算法的基本性质 简单地说,算法就是进行操作的方法和操作步骤。例如,菜谱实际上是做菜肴的算法,乐谱实际上是演奏的算法,计算机程序是用某种程序设计语言描述的解题算法。通常认为算法有如下一些性质:(1)有效性 有效性指算法所规定的操作都应当是能够有效执行的。例如,对于操作汽车的算法,有效的操作就是加速、刹车、换档、转动方向盘、鸣笛等,要让汽车执行“跳起”就是无效的操作。同样,一个计算机算法必须是计算机能够执行的。(2)确定性 确定性具有两重意义:一是所描述的操作应当具有明确的意义,不应当有歧义性。例如,不能发出这样的操作指令:“执行一个算术操作”。因为它既没有指出算术操作的类型,
9、也没有指出操作数。确定性的另一重意义:操作作序列只有一个初始动作,序列中每一动作仅有一个后继动作;序列终止表示问题得到解答或问题没有解答,不能没有任何结论。(3)有穷性 有穷性指算法所规定的操作序列必须在允许的时间内结束。例如,一个计算机算法要执行100年以上,就失去有穷性。,下面对其中一些主要符号作简要说明。平行四边形表示数据,其中可注明数据名称、来源、用途或其它的文字说明。处理矩形表示各种处理功能。例如,执行一个或一组特定的操作,从而使信息的值、信息形式或所在位置发生变化。矩形内可注明处理名称或其简要功能。预定义过程带有双竖边线的矩形,表示已命名的处理。该处理为在另外地方已得到详细说明的一
10、个操作或一组操作。例如库函数或其它已定义的函数等。矩形内可注明特定处理名称或其简要功能。判断菱形表示判断。菱形内可注明判断的条件。它只有一个入口,但可以有若干个可供选择的出口,在对定义的判断条件求值后,有一个且仅有一个出口被选择。求值结果可在表示出口路径的流线附近写出。流线直线表示执行的流程。当流程自上向下或由左向右时,流程线可不带箭头,其它情况应加箭头表示流程。端点。扁圆形表示转向外部环境或从外部环境转入的端点符。例如,程序流程的起、始点。注解。注解是程序的编写者向阅读者提供的说明。注解符由纵边线构成,它用虚线连接到被注解的符号或符号组上。,图3.3(a)、(b)、(c)中的虚线框,分别为用
11、流程图表示的三种基本流程控制结构。这三种基本结构有一个明显的特征:单入口和单出口。从整体上看都相当于一个处理框。用它们所组成的程序来龙去脉十分清晰。,(a)顺序结构,(b)选择结构,(c)重复结构,图3.3 用流程图描述的三种流程基本结构,例3.1 用流程图描述从三个数中取最大数的算法。从三个数中取最大数的算法思路是:假定这三个数是a,b,c,则首先可以比较a,b两数,从中选大者;然后再用这个大数与数c比较,从中取大者。这时得到的大数,就是三个数中的最大数。这个算法用流程图描述如图3.4(a)所示。通常,求解一个问题的算法不是唯一的。例如图3.4(b)也是一个三数中取大的算法。它的基本思路是,
12、先设max=0,然后依次输入i个数,每输入一个数,与max比较一次,把大的放入max中。当输完i个数时,max中存放的就是这i个数中的最大数。这里,i是一个计数器,用于统计输入数的个数,所以每输入一个数,要执行一次自增操作。这个算法与图3.4(a)所示算法的不同在于:算法结构不同:图3.4(a)所示算法采用了两个选择结构,而图3.4(b)所示算法采用了一个循环结构和一个选择结构。算法的灵活性不同:图3.4(b)中的算法可以用来求任意个数中的最大数。,(a)一个三数中取大的算法,(b)另一个三数中取大的算法,图3.4 三数中取大算法的流程图描述,用流程图表示算法灵活、自由、形象、直观,可以表示任
13、何算法。它允许用流线使流程任意转移。但这却是程序设计中的一个隐患,程序中如果允许流程毫无限制地任意转移,就会使程序如同一团乱麻一样难以阅读和维护,如图3.5所示。这种程序叫做BS程序(a Bowl of Spaghetti的缩写,意为“一碗面条”似的)。,图3.5 无限制地使用流线形成BS流程结构,结构化程序设计主张限制这种无规律的任意转向,采用三种基本结构作为构成程序的基本单位。这样就必须限制流线的使用。,2.N-S图描述灵活的流线是程序中隐藏错误的祸根。针对这一弊病,1973年美国学者I.Nassi和B.Shneiderman提出了一种无流线的流程图,称为N-S图。它的三种基本结构如图3.
14、6所示。N-S图的每一种基本结构都是一个矩形框,整个算法可以像堆积木一样堆成。其中,(a)为三个操作组成的顺序结构;(b)为二分支的选择结构;(c)为当型重复结构,即当命题P 为“真”时,就重复执行S。由于N-S图中没有了流线,所以绝对不会出现由于乱用流线造成的BS算法结构。,(c)当型重复结构,(a)顺序结构,(b)选择结构,图3.6 用N-S图描述三种基本流程结构,图3.7(a)、(b)给出了用与图3.4(a)、(b)流程图对应的N-S图。注意,空白的处理框表示没有操作。,(a)采用选择结构,(b)采用当型重复结构,图3.7 三数中取大算法的N-S图描述,3.伪代码伪代码(pseudo c
15、ode)是用介于自然语言与计算机语言之间的文字符号算法描述的工具。它无固定的、严格的语法规则,通常是借助某种高级语言的控制结构,中间的操作可以用自然语言(如中文或英文),也可以用程序设计语言,或使用自然语言与程序设计语言的混合体。一般专业人员习惯用伪代码进行算法描述。下面是与图3.7相对应的三数中取大算法的伪代码描述。(1)与图3.7(a)相对应的三数中取大算法的伪代码描述:,输入a,b,c;if(a=b)max=a;else max=bif(max=c)输出max;else 输出c;,(2)与图3.7(b)相对应的三数中取大算法的伪代码描述:,初始化:mac=0,i=1;当(i=max)ma
16、x=n;)输出max;,(3)与图3.7(c)相对应的三数中取大算法的伪代码描述:,初始化:mac=0,i=1;(输入n;i+;if(n=max)max=n;)直到(i3)输出max;,3.1.3 自顶向下,逐步细化的算法设计过程算法设计是一个的智力过程。设计复杂问题的算法更是一个高强度的智力劳动。实践证明,保证算法设计正确的一个方法是要让问题的复杂性能够在人的智力容易控制范围之内。为此人们提出了一种自顶向下、逐步细化的算法设计方法。按照这种方法,解题之初不要一下子就力图触及到问题解法的细节,而应当从问题的全局出发,给出高度概括、抽象的算法,通常是把这些问题的求解分成可以独立求解的若干子问题;
17、接着对每一个子问题,再进行分解;最后对不可再分的子问题分别设计算法,而且设计的过程也是从概括逐步细化。一般说来,上层解决的是“做什么”的过程,逐步细化的过程是解决“怎么做”的过程。在用伪代码描述算法时,随着逐步细化,最终可以用程序设计语言代替算法中的伪代码。等到全部伪代码都用某种程序设计语言描述了,程序设计也就基本完成了。,例3.2 用自顶向下、逐步细化的方法设计三数中取大算法的过程。这里,仅考虑采用伪代码描述的方法。(1)首先,把该问题分析为:,s1:输入三数a,b,c;s2:从a,b,c中找出大数赋给max;s3:输出max。,这一阶段算法是用汉语描述的,描述了按顺序要完成的三个“做什么”
18、。可以用s1,s2,s3代表第1步、第2步、第3步。s是步(step)的缩写。(2)在前一阶段的基础上考虑各个“做什么”的实现途径,把算法细化为:,s1:调用scanf()函数;s2:设计一个函数max3(a,b,c);s3:调用printf()函数。,这一阶段是用混合语言写算法。,(3)写主函数的条件已经成熟。,int main(void)/*三数中取大数*/float a,b,c,max;float max3(float x,float y,float z);printf(Input 3 numbers a b c:);scanf(%f%f%f,(4)设计max 3()的算法。仍按逐步细化
19、的方法,先给出概要算法。设三个参数为float x,float y,float z。,s2.1:从x与y中取大数送m中;s2.2:从m与z中取大数送m中;s2.3:返回m给主调函数。,进一步细化得,s2.1:if(xy)m=x;elsem=y;s2.2:if(mz)m=m;elsem=z;s2.3:return(m);,这就很容易用C语言立刻写出函数max3()了:,float max 3(float x,float y,float z)float m;if(xy)m=x;elsem=y;if(mz)m=m;elsem=z;return(m);,一次执行结果:,Input 3 numbers
20、a b c:34.7 45.9 678The max is:678.000000,3.2 判断,3.2.1 命题的“真”、“假”与C语言中的逻辑值不论是选择结构,还是循环结构,流程的改变都是由判断决定的。即需要根据判断决定选择,根据判断决定是否循环以及循环的结束。通常,判断是针对命题的“真”、“假”进行的。例如,下面是一些命题:(行驶中)前面的交通信号灯是红色的。今天下雨。ab。abc,即ab 且bc。ab 或cb。如果a不能被b整除。这些命题的值都只能是一个逻辑(布尔)值:“真”或“假”。早期的C语言不提供专门的逻辑(布尔)类型,规定用非0值表示“真”,用0值表示“假”。这样,不管任何表达式
21、,只要其值为非0(包括负数),就说明其为“真”;只要其值为0,就说明其为“假”。从而给判断带来很大的灵活性。在C99中,增加了_Bool类型,并增加了头文件,在其中定义了宏bool、true和false,用true存储1,用false存储0。,3.2.2 关系运算与关系表达式 关系表达式和逻辑表达式是C语言中描述命题的两种基本形式。1.C语言的关系运算符关系运算是指对两个表达式值的大小比较。C语言中提供有如下6个关系运算符:(大于)=(大于或等于)(小于)=(小于或等于)=(等于)!=(不等于)后两个(=和!=)的优先级小于前4个,但它们都低于纯算术类,高于赋值类,并且它们结合方式都是从左向右
22、的。例如:a+bc+d 应理解为(a+b)(c+d),2.关系运算符的值关系表达式的值只有两个:关系表达式成立,即为“真”(通常为1);关系表达式不成立,即为“假”(0)。例如对于声明:int x=2,y=3,z;表达式 x=y 的值为0。而表达式 xy 的值为1。那末表达式 z=3-1=x+1=y+2 的值呢?在这个表达式中有赋值、关系、算术三种运算。其中“赋值”的优先级最低,其次为“关系”,“算术”的优先级最高。因此,先进行算术运算得 z=2=3=5 然后计算2=3,其值为0(假),得 z=0=5 再计算0=5,值为1(真),故有z的值为1。,应当注意:(1)表达式5278在数学上是不允许
23、的,而在C中是允许的。按自左而右的结合求解:52值为1;17值为0;08的值为0。即整个关系表达式的值为0。(2)由于关系表达式的值是整型数0或1,故也可以将其看成是一种整型表达式。例如,若有:int i=1,j=7,a;a=i+(j%4!=0);由于j%4的值为3,而3!=0的值为1(真),故a的值为2。但这种表达式的含义不易被理解,初学时不宜多用。,(4)在判定两个浮点数是否相等时,由于存储上的误差,会得出错误的结果。例如:1.0/3.0*3.0=1.0 在数学上显然应该是一个恒等式,但由于1.0/3.0得到的值的有效位数是有限的,并不等于0.3333333333333,因此上面关系表达式
24、的值为0(假),并不为1(真)。所以应避免对两个实数表达式作“相等”或“不相等”的判别。上式可改写为:fabs(1.0/3.0*3.0-1.0)1e-5 fabs是求绝对值函数。只要1.0/3.0与1.0之间的差小于10-5(或一个其它的很小的数),就认为1.0/3.0*3.0与1.0相等。(5)要表示x在区间a,b中,在数学中使用表达式axb。但在C语言中使用表达式“a=x=b”会与原来的意义不同。假设a=0;b=0.5。若x=0.3,则执行a=x=b时先求出“a=x”的值得1(真),再计算“1=b”得0(假)。因此,为了判别x是否在a,b范围内,应写成:a=x&x=b“&”是下面将要介绍的
25、逻辑运算符“与”。a=x的值为1(真)且x=b的值亦为1(真),则整个表达式的值为1(真)。,3.2.3 逻辑运算与逻辑表达式1.逻辑运算的基本概念 C语言有三个逻辑运算符,它们是:&(逻辑与)|(逻辑或)!(逻辑非)为了说明“与”、“或”、“非”的概念,先看一下图3.8。当图3.8(a)中的开关a和b都合上时,灯泡c才亮。这就是“与”的概念,即只有a和b均为真时,c才为“真”(1)。在C语言中,“与”运算用“&”运算符表示。下面是逻辑“与”的简单应用:,if(a,图3.8(b)中c灯亮的条件是a“或”b之一合上(二者都合上也行),即至少a或b之一为“真”(1)。在C语言中,“或”运算用“|”
26、运算符表示。下面是逻辑“或”的简单应用:,if(a|b)c=1;else c=0;,(a)逻辑“与”,(b)逻辑“或”,(c)逻辑“非”,图3.8 三个基本逻辑运算,从图3.8(c)中可以看出,当a合上时c灯不亮,只有a不合上灯才亮,即a“非”,c才为“真”(1)。在C语言中,“非”运算用“!”运算符表示。下面是逻辑“非”的简单应用:,if(!a)c=1;elsec=0;,&和|是二元运算符,结合方向为自左至右;!为一元运算符,结合方向为自右至左。&和|的优先级低于关系运算符,而!的优先级高于关系运算符。例如表达式!31,应先计算!3,得0;再计算01,得0。,2.常用的逻辑运算规律 逻辑运算
27、中有很多有趣的规律。例如:(1)在一个&表达式中,若&的一端为0,则不必再计算另一端,该表达式的值肯定为0(在C语言中由于&是从左向右结合的,所以只考虑左端,即当&号的左端为0时,不再计算其右端),可以把它记为:0&a=0(2)在一个|表达式中,若|的一端为1,则不必计算另一端,该|表达式的值必为1。现把它记为:1|a=1 诸如此类关于表达式的值的规律有如下一些:0|a=a1&a=a1|a=10&a=0 a|!a=1 0&!a=0 以及 a|a=aa&a=a!(a|b)=!a&!b!(a&b)=!a|!b!(!a)=a记住这些规律,能使复杂的逻辑运算简化、清晰。,表3.1为基本逻辑运算的真值表
28、:两个操作数为不同的逻辑值时,基本逻辑运算的运算结果。,表3.1 基本逻辑运算的真值表,3.3 选择型程序设计,选择型程序描述了求解规则:在不同的条件下所应进行的相应操作。下面分别介绍C语言中用来实现选择结构的三种语句。3.3.1 ifelse结构的应用 if(表达式)语句1 else 语句2ifelse构造了一种二路分支选择结构,它具有如下格式:这里,语句1和语句2就是两路分支。执行这个结构,首先对表达式进行判断,若为“真”(非零),就执行if分结构(语句1);否则(值为0),就执行else分结构(语句2)。,if(表达式)语句1else语句2,例3.3 求一个数的绝对值。设有任意数x,它的
29、绝对值为 判断内容:x0是否成立。两路分支:x=x(当x0);x=-x(当x0)C函数如下:,/*文件名:ex030301.c*/*求绝对值*/double abstr(double x)if(x0.0)x=-x;elsex=x;return(x);,下面是函数abstr被调用执行的情况:,#include int main(void)double a,abstr(double a);printf(Enter real number a please:);scanf(%1f,运行情况如下,Enter real number a please:-98.7654 abstr(-98.765400)
30、=98.765400,C语言还允许使用缺省else分结构的ifelse结构。其形式为:如上述abstr()函数可以改写为:,double abstr(double x)if(x0.0)x=-x;return(x);,这种结构称为不平衡if结构。它不如平衡的if结构容易理解和清晰。下面看一个稍复杂的例子。,if(表达式)语句,例3.4 三数中取大数。有人按照图3.9所示的算法逻辑设计了下面的函数:,/*文件名:ex030401.c*/*三数取大*/float max3(float x,float y,float z)float max=x;if(zy)if(zx)max=z;elseif(yx)
31、max=y;return(max);,然而,它被main()函数调用时的的一次运行情况如下:,Enter 3 real numbers a,b,c:12.34 5.67 34.56 The max is 12.300000,这就因为使用不平衡if结构时,没有搞清ifelse结构的语法规则。,图3.9 例3.4的设计者使用的算法,C语言不规定程序的书写格式,允许以灵活的格式书写。在编译时完全不考虑程序的书写格式,只凭语法规则来确定程序中的逻辑关系。当程序中存在嵌套的ifelse结构时,由后向前使每一个else都与其前面的最靠近它的if配对。如果一个else的上面又有一个未经配对的else,则先处
32、理上面的(内层的)else的配对。这样反复配对,直到把全部else用完为止。按照这一规则,例3.4中的函数max3实际上具有如图3.10所示的逻辑关系。显然,这是一个错误的算法,因为当x,y,z分别为(2,3,1)和(1,3,2)时,结果是错误的。,图3.10 例3.4程序实际使用的算法,通过这个例子可以看到:(1)不平衡的ifelse结构会增加阅读和理解程序的困难。(2)正确的缩进格式(即锯齿形书写格式)可以帮助人们理解程序,但错误的缩进格式反而会使人迷惑。(3)不要太相信自己的判断,要严格按语法关系检查程序。在不易弄清的地方可以加花括号来保证自己构思的逻辑关系的正确性。如上述程序可以改写为
33、:,float max3(float x,float y,float z)float max=x;if(zy)if(zx)max=z;elseif(yx)max=y;return(max);,例3.5 求一元二次方程ax2+bx+c=0的根。算法设计:一元二次方程式的根有下列情况:当a=0,b=0时,方程无解;当a=0,b0时,方程只有一个实根-c/b;当a0时,方程的根为x=(-bb2-4ac)/(2*a)。其中,当b2-4ac0时有两个实根;当b2-4ac0时,有两个虚根。图3.12为上述算法的N-S图表示,它把上述逻辑关系表示得更为清晰。,图3.12 求一元二次方程根的算法,/*文件名:
34、ex030501.c*/*求解一元二次方程*/include void solv_quadr_equa(float a,float b,float c)if(a=0.0)if(b=0.0)printf(no answer due to input errorn);elseprintf(the single root is%fn,-c/b);elsedouble disc,twoa,term1,term2;disc=b*b-4*a*c;twoa=2*a;term1=-b/twoa;term2=sqrt(fabs(disc)/twoa;if(disc0.0printf(complex root:n
35、 real part=%f,imag part=%fn,term1,term2);elseprintf(real root:n root1=%f,root2=%fn,term1+term2,term1-term2);,由此N-S图可以编写出以下的函数:,程序第2行第一个单词void表示函数solv_quadr_equa的类型为“空类型”,即该函数无返回值,因为求出的方程解都是在执行该函数时输出的,不需要将方程解带回主程序中输出。所以函数值为“空”。程序solv_quadr_equa 的第15行中fabs是一个标准数学函数名,输出一个实数的绝对值。请读者自己编写出测试此函数的main函数。,3.
36、3.2 if-else if结构的应用if(表达式1)if-else if是一种多分支选择结构,用流程图描述如图3.13所示,用N-S图描述如图3.14所示,C语言中的格式如下:,if(表达式1)语句1else if(表达式2)语句2else if(表达式n)语句nelse语句n+1,图3.13 if-else if结构的流程图描述,图3.14 else if结构的N-S图描述,例3.6 根据百分制分数决定成绩的等级:80分以上为A级;70分及以上,80分以下,B级;60分及以上,70分以下,C级;60分以下,D级。图3.15为用if-else if结构描述的上述规则。,图3.15 根据百分制
37、分数决定成绩的等级,程序如下:,/*文件名:ex031701.c*/*按成绩分等*/include int main(void)float score;printf(“Input a scre:”);scanf(“%f”,可以看到,if-else if是通过一连串的判断,来寻找问题的解。它排列了一系列操作,每一种操作都是在相应的条件下才能执行的。该结构开始执行后,便依次去对各个条件进行判断测试,符合某一条件,则转去执行该条件下的操作,其它部分将被跳过;如无一条件为真,就执行最后一个else所指定的操作。这个else可以看作“其它”。若最后一个else不存在,并且所有条件的测试均不成功,则该el
38、se if结构将不执行任何操作。,3.3.3 switch结构的应用 switch是一种多分支选择结构,其形式如右。switch结构也称标号分支结构,它用花括号括起了一系列case子结构。每个case子结构都以一个常量表达式作为标志的标号,并按照下面的规则匹配:计算switch的判断表达式,并以此值去依次找与之相等的case标号值,找到后就将流程转到该标号处,执行后面各语句;如果找不到符合的case子结构,就只执行default子结构中的语句序列。default子结构对于switch结构不是必须的。当没有default子结构,并且没有相符合的case时,该switch结构就不被执行。,swit
39、ch(表达式)case 常量表达式1:语句序列1case 常量表达式2:语句序列2case 常量表达式n:语句序列ndefault:语句序列n+1,请注意如果switch的判断表达式的值与case常量表达式i的值相等(称为匹配),在执行后面的语句序列i之后,并不立即退出switch结构,而是继续执行语句序列i+1,语句序列i+2,语句序列n语句序列n+1如图3.16所示,这种流程往往不是编程者所希望的。编程者希望在执行匹配的常量表达式后面的语句序列i之后,应立即退出switch结构。为了解决这一问题,可以在各语句序列后面加一条break语句,使流程脱离switch结构。如图3.17所示。,这里
40、,break语句的作用是中断该switch结构,即将流程转出switch结构。所以,执行switch结构的就相当于只执行与判断表达式相匹配的一个case子结构中的语句。其实,可以将break看作为语句序列中必要的成分(位置在语句序列中的最后)。,switch(表达式)case 常量表达式1:语句序列1break;case 常量表达式2:语句序列2break;case 常量表达式n:语句序列nbreak;default:语句序列n+1break;,例3.7 将一个月份数字转换成英文月份名称。用函数实现这个功能如下:,/*文件名:ex030701.c*/*输出月份名称*/void MonthNam
41、e(int month)(switch(month)case 1:priontf(“Januaryn”);break;case 2:priontf(“Februaryn”);break;case 3:priontf(“Marchn”);break;case 4:priontf(“Apriln”);break;case 5:priontf(“Mayn”);break;case 6:priontf(“Junen”);break;case 7:priontf(“Jaulyn”);break;case 8:priontf(“Augustn”);break;case 9:priontf(“Septemb
42、ern”);break;case 10:priontf(“Octobern”);break;case 11:priontf(“Novembern”);break;case 12:priontf(“Decembern”);break;default:priontf(“Illegal monthn”);break;return;,这个例子中,每一个case子结构的最后一个语句都是break。下面的例子中,是部分case子结构中有break语句。例3.8 测试是数字、空白还是其它字符的函数(假设测试的对象只限于以上几种字符)。,/*文件名:ex030801.c*/*测试字符类型*/int test_
43、char(int c)switch(c)case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:,printf(its a digit n);break;case:case n:case t:printf(it s a whiten);break;default:printf(it s a charn);break;,这个函数的执行过程如下:switch的条件表达式是一个已有整数值的c(今设c=8),于是从上至下找各case后的常数0,1,2,3,4,5,6,7,8,于是从case 8入口开始执行下面的语句(采
44、用了结构1)。case 8后面没有语句,就执行case 9后面的printf函数语句,然后遇到break语句时跳出switch结构。当测试都失败时,即default以上的各case都不匹配,default则囊括了“除上述各case之外的一切情形”,于是执行default子结构,直到遇到break退出switch结构。从语法上讲,default子结构中的break语句并不是必须的,执行完default子结构中的各语句后,则后面已无可执行的语句了,会自动退出switch结构。这里使用了一个break语句是为了排列上的整齐及理解上的方便。default子结构考虑了各case所列出的情形以外的其它情形
45、。这样就能在进行程序设计时,把出现频率低的特殊情形写在case的后面,而将其余情况写在default后面作“统一处理”。如果只考虑对个别情况的处理,则可将各个情况分别写在各个case的后面,此时default子结构可以省略。,使用switch结构须注意以下几点:(1)一个switch结构的执行部分是一个由一些case子结构与一个可缺省的default子结构组成的复合语句,它们位于一对花括号之中。(2)switch的判断表达式只能对整数求值,可以使用字符或整数,但不能使用浮点表达式。case表达式应当是整型常数表达式,不能含有变量与函数的常数表达式。例如可以是:,case 3+4:,但不允许写为
46、:,int x=3,y=4;switch(z)case x+y:,(3)一个switch结构中不可以出现两个具有相同值的常量表达式。例如:,case 3+2:case 8-3:,是不允许的。(4)switch的匹配测试,只能测试是否相等,不能测试关系或逻辑。(5)C89要求C编译系统应当实现一个switch最少可以包含257个case子结构,而C99要求最少支持1023个case子结构。(6)switch结构允许嵌套。,/*文件名:ex030901.c*/*联想猜词游戏*/include stdio.hint main(void)int c;c=getchar();getchar();/*为接
47、收一个字符,再接收一个分隔符换行或空格*/,例3.9 联想猜词游戏 这个游戏是:游戏者心中想好一个单词,并先在键盘上输入第1个字母,然后让计算机猜所选的是哪个单词;如果计算机显示出的单词不只一个,游戏者就要再输入第2个字母,如此操作,知道计算机猜出这个单词或得出“猜不出”的结果为止。下面的程序以猜“计算机语言名称”为例。,switch(c)case a:case A:printf(Ada,Algol?n);c=getchar();getchar();switch(c)case d:case D:printf(Ada n);break;case l:case L:printf(Algol n);
48、break;default:printf(input errorn);break;break;,case b:case B:printf(Basic,BCDL?n);c=getchar();getchar();switch(c)case a:case A:printf(Basicn);break;case c:case C:printf(BCDLn);break;default:printf(I am sorry!n);break;break;,case c:case C:printf(C,Cobol,C+,C#?n);c=getchar();getchar();switch(c)case c
49、:case C:printf(Cn);break;case o:case O:printf(Coboln);break;case+:printf(C+n);break;case#:printf(C#n);break;,default:printf(I am sorry!n);break;break;default:printf(I am sorry!n);break;return 0;,这是一个简单的switch嵌套。输入2个字母后就可找出唯一的单词。如果单词量大而且两个字母还不能区分出单词时,嵌套层次就要增加,程序也就复杂了。,条件表达式 条件表达式也称问号表达式,它的一般形式为:,表达式1
50、?表达式2:表达式3,它的操作过程为:若表达式的值为真(非0),则以表达式2的值作为该条件表达式的值;否则取表达式3的值作为该条件表达式的值。,例3.10 计算a+b的值。,/*文件名:ex031001.c*/*计算绝对值*/#include int main(void)/*计算a+b的值*/float a,b;printf(input 2 reals please:);scanf(%f%f,运行情况如下:,input 2 reals please:10-9010.000000+-90.000000=100.000000,printf()函数中b=0?a+ba-b,当b0(b为正值)时,取值a