第五章 选择结构的程序设计.docx
《第五章 选择结构的程序设计.docx》由会员分享,可在线阅读,更多相关《第五章 选择结构的程序设计.docx(16页珍藏版)》请在冰点文库上搜索。
第五章选择结构的程序设计
5.1用if语句设计选择结构程序
C语言中的if语句有两种形式:
简单if语句和if_else语句。
5.1.1简单if语句
简单if语句形式:
if(表达式)语句1
功能:
计算表达式的值,若为"真",则执行语句1;否则将跳过语句1执行if语句的下一条语句。
如图5-1所示。
说明:
括号中的表达式表示控制条件,表达式的值非零为"真",零为"假"。
语句1从语法上应是一条语句,若需要在此执行多条语句,必须用大括号将它们括起来,构成复合语句,这样,语法上它仍然是一条语句。
例5-1:
求给定整数的绝对值。
求x绝对值的算法很简单,若x≥0,则x即为所求;若x<0,则-x为x的绝对值。
如图5-2所示。
程序中首先定义整型变量x和y,其中y存放x的绝对值。
输入x的值之后,执行y=x;语句,即先假定x≥0,然后再判断x是否小于0,若x<0,则x的绝对值为-x,将-x赋给y(y中原来的x值被"冲"掉了)后输出结果。
若x≥0,则跳过y=-x;语句,直接输出结果。
此时y中的值仍然是原x的值。
#include"stdio.h"
main()
{intx,y;
scanf("%d",&x);
y=x;
if(x<0)y=-x;
printf("x=%d,|x|=%d\n",x,y);
}
运行程序:
输入:
-5↙
输出:
x=-5,|x|=5
例5-2:
求4个数中的偶数之和。
#include"stdio.h"
main()
{inta,b,c,d,s;
printf("Pleaseinputa,b,c,d:
\n");
scanf("%d,%d,%d,%d",&a,&b,&c,&d);
s=0;/*存放和的变量先置初值0*/
if(a%2==0)s=s+a;/*能被2整除是偶数,依次检查各变量并将偶数累加到s中*/
if(b%2==0)s=s+b;
if(c%2==0)s=s+c;
if(d%2==0)s=s+d;
printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
printf("s=%d\n",s);
}
运行程序:
Pleaseinputa,b,c,d:
输入:
12,35,6,11↙
输出:
a=12,b=35,c=6,d=11
s=18
令变量s(存放和值)的初值为0,注意,这一步骤不能省略,否则不能保证s的初值为0。
在累加之前,若s的初值不为0,则最终的和值中含有s的非0初值,得到错误结果。
求n个数的和可以采用两种算法:
(以n=4为例)
①s=a+b+c+d
②s=0;
s=s+a;s=s+b;s=s+c;s=s+d;
第一种方法虽然简单明了,但有明显不足。
在本例中,要求累加4个数中的偶数,第一种方法不能进行条件的判断,无法采用;又如,当n很大时,不可能把n个数依次写出。
而第二种方法中,累加是分开执行的,因此可以在累加前进行条件的判断;在后续章节中可以看到,第二种方法结合循环和数组就可以解决n很大的求和问题。
5.4用switch语句设计多分支结构程序
例5-8中已经遇到多分支选择结构,虽然用嵌套if语句也能实现多分支结构程序,但分支较多时显得很繁琐,可读性较差。
C语言中,switch语句专用于实现多分支结构程序,其特点是各分支清晰而直观。
5.4.1switch语句
switch语句调用形式:
switch(表达式)
{case常量表达式1:
语句1
case常量表达式2:
语句2
……
case常量表达式n:
语句n
default:
语句n+1
}
功能:
首先计算表达式的值,然后依次与常量表达式i(i=1,2,……,n)比较,若表达式的值与常量表达式j相等,则从常量表达式j处开始执行(执行入口),直到switch语句结束。
若所有的常量表达式i(i=1,2,……,n)均不等于表达式,则从default处开始执行。
说明:
①switch后面括号中可以是任何表达式,取其整数部分与各常量表达式进行比较。
②常量表达式中不能出现变量,且类型必须是整型、字符型或枚举型,各常量表达式互不相同。
③语句i可以是一条或多条语句,多条语句时不必用{}将它们括起来。
语句i处也可以没有语句,程序执行到此会自动向下顺序执行。
④default语句一般出现在所有case语句之后,也可以出现在case语句之前或两个case语句之间。
default语句可以缺省。
例5-9:
用switch语句实现例5-8。
为了区分各分数段,将[0,100]每10分划为一段,则x/10的值为10,9,……,1,0,它们表示11段:
0---9为0段,10---19为1段,……,90---99为9段,100为10段。
用case后的常量表示段号。
例如,x=76,则x/10的值为7,所以x在7段,即70≤x<79,属于C级。
若x/10不在[0,10],则表明x是非法成绩,在default分支处理。
#include"stdio.h"
main()
{intx;
printf("Pleaseinputx:
\n");
scanf("%d",&x);
switch(x/10)
{case10:
printf("x=%d→A\n",x);
case9:
printf("x=%d→A\n",x);
case8:
printf("x=%d→B\n",x);
case7:
printf("x=%d→C\n",x);
case6:
printf("x=%d→C\n",x);
case5:
printf("x=%d→D\n",x);
case4:
printf("x=%d→D\n",x);
case3:
printf("x=%d→D\n",x);
case2:
printf("x=%d→D\n",x);
case1:
printf("x=%d→D\n",x);
case0:
printf("x=%d→D\n",x);
default:
printf("x=%ddataerror!
\n",x);
}
}
运行程序:
输入:
65↙
输出:
x=65→C
x=65→D
x=65→D
x=65→D
x=65→D
x=65→D
x=65→D
x=65dataerror!
第一行输出正确,但后7行输出是多余的。
原因何在?
x/10的值为6,从case6处开始执行,输出x=65→C,若就此结束switch语句的执行,则结果正确。
但是,根据switch语句的执行流程,x/10的值与case6匹配后,case6处是执行的入口,以后将顺序执行case7,case8,……后面的语句(除非遇到终止执行的指令)。
因此在输出正确结果后,立即终止switch语句的执行才能达到目的。
如何终止switch语句的执行呢?
C语言中提供的break语句可以做到这一点。
5.5无条件转向语句
C语言中的goto语句可以转向同一函数内任意指定位置执行,称为无条件转向语句。
goto语句调用形式:
goto语句标号;
……
语句标号:
功能:
goto语句无条件转向语句标号所标识的语句执行。
它将改变顺序执行方式。
说明:
①语句标号用标识符后跟冒号表示。
例如:
……
gotok;
……
k:
②goto语句与相应的语句标号必须处在同一个函数中,不允许跨两个函数。
下面的用法是错误的:
main()ff()
{ ……{……
gotok; k:
…… ……
} }
例5-11:
输入三角形3条边的边长并输出。
作为三角形的3条边长,必须满足任意两个边长之和大于第3个边长。
若输入的3个边长不满足该条件,程序在显示提示信息后利用goto语句自动转到输入函数调用语句,要求用户重新输入,直到输入的3个边长满足条件。
main()
{intx,y,z;
k:
scanf("%d,%d,%d",&x,&y,&z);
if(x+y<=z||x+z<=y||y+z<=x)
{printf("dataerror!
Inputagain\n");
gotok;/*x、y、z不满足三角形边长要求,转k:
,重新输入*/
}
printf("x=%d,y=%d,z=%d\n",x,y,z);
}
运行程序:
输入:
1,2,3↙
输出:
dataerror!
Inputagain
输入:
2,2,3↙
输出:
x=2,y=2,z=3
在例5-8中,输入的成绩是非法成绩时,只能终止程序的运行。
用下面带goto语句的程序段可以要求用户重新输入合法成绩:
k:
scanf("%d",&x);
if(x<0||x>100)
{printf("dataerror!
Inputagain\n");
gotok;
}
……
由于goto语句转移的任意性,使程序流程毫无规律,可读性较差。
所以,一般情况下结构化程序设计中不主张使用goto语句。
但在某种场合下,使用goto语句可以提高效率。
例如,在嵌套switch语句的内层switch语句中,利用break语句只能一层一层地退出,若采用goto语句,可以一次退出多层switch语句。
如下程序段中,要从内层switch(y==0)语句的case1处退出switch(x)语句,利用break语句先退出switch(y==0)语句,然后利用switch(z)语句case2的break语句退出switch(z)语句,再利用switch(x)语句的case0最后的break退出switch(x)语句。
同样处在内层switch(y==0)语句的case0处的goto语句,却能直接转出switch(x)语句,执行语句标号k处的语句,只需一步就退出switch(x)语句,其效率不言自明。
switch(x)
{case0:
switch(z)
{case2:
switch(y==0)
{case1:
printf("*");break;
case0:
printf("+");gotok;
}
break;
case1:
printf("@");break;
default:
printf("===");
}
break;
case1:
printf("$");break;
default:
printf("!
!
!
");
}
k:
……
5.6应用举例
例5-12:
输入3个数,输出其中最小者。
设3个数分别是a、b和c,先求a、b中较小者并记为min,然后再用min与c进行比较,取其中较小者作为最后结果。
采用以前介绍的条件运算符可以解决这个问题。
#include"stdio.h"
main()
{inta,b,c,min;
printf("Pleaseinputa,b,c\n");
scanf("%d,%d,%d",&a,&b,&c);
min=a
a:
b;
min=minmin:
c;
printf("min=%d\n",min);
}
运行程序:
输入:
12,24,8↙
输出:
min=8
程序中两条关于min的赋值语句也可以换成本章介绍的if_else语句结构:
if(a
elsemin=b;
if(min>c)min=c;
例5-13:
输入3个数,按从大到小的顺序输出。
设3个数分别是a、b和c,把它们中最大者存放在a中,把次大者存放在b中,c中存放最小者。
然后依次输出a、b和c。
#include"stdio.h"
main()
{inta,b,c,t;
printf("Pleaseinputa,b,c\n");
scanf("%d,%d,%d",&a,&b,&c);
if(a
if(a if(b printf("%d>=%d>=%d\n",a,b,c);
}
运行程序:
输入:
12,24,8↙
输出:
24>=12>=8
例5-14:
输入两个整数,若它们的平方和大于100,则输出该平方和的百位数以上(包括百位数字)的各位数字,否则输出两个整数的和。
#include"stdio.h"
main()
{inta,b,c,d;
printf("Pleaseinputa,b\n");
scanf("%d,%d",&a,&b);
c=a*a+b*b;
if(c>100)
{ d=c/100;
printf("%d→%d\n",c,d);
}
else
printf("a+b=%d\n",a+b);
}
运行程序:
输入:
11,10↙
输出:
221→2
输入:
3,2↙
输出:
a+b=5
若a、b的平方和大于100时,要求输出百位数以上的数字,即去掉个位及十位数字后的数。
一个数k,取百位数以上(含百位数)的数字为k/100;取百位数以下(不含百位数)的数字为k%100。
如:
12345,12345/100的值为123,即百位数以上(含百位数)的数;12345%100的值为45,即百位数以下(不含百位数)的数。
例5-15:
求一元二次方程ax2+bx+c=0的根。
对一元二次方程ax2+bx+c=0,要考虑其系数a、b、c各种可能的取值情况。
若a为0,则原方程蜕化为一元一次方程bx+c=0,所以当b不为0时,x=-c/b;
当a不为0时,有两个根(实根或复根):
若b*b-4*a*c≥0,有两个实根:
x1,2=(-b±)/(2*a)
若b*b-4*a*c<0,有两个共扼复根:
x1,2=-b/(2*a)±i/(2*a)
在第一章中介绍了"自顶向下,逐步细化"的程序设计方法,这种方法可以先全局后局部。
先整体后细节,先抽象后具体,其优点在于问题考虑全面,程序结构清晰,层次分明,可读性好。
一般,求解问题的程序可以分成三部分:
输入部分、计算处理部分和输出部分。
输入部分负责输入必要的原始数据,计算处理部分根据问题的算法求解,得到的结果由输出部分显示或打印。
因此,本题的顶层设计就是把问题划分为三个模块:
M1模块用来输入方程的系数a、b、c;M2模块的功能是计算方程的根;M3模块负责输出结果。
它们的执行顺序是M1,M2,M3。
如图5-8所示。
对这些模块还可以进行细化。
对M1模块,功能是输入方程的三个系数,比较简单,不必再细分了。
M2模块的功能是求根,当二次项系数a为0时,方程蜕化成一次方程bx+c=0,x=-c/b。
求根方法不同于二次方程,可见M2模块比较复杂,需要进一步细化。
M2模块可以再细分成M21和M22两个模块,M21模块的功能是一次方程的求根,无需细分。
M22模块的功能是二次方程的求根。
实际上,M22模块还可以再分解为求实根和求复根两个子模块,这里就不再细分了,因为求实根和求复根在同一个模块中处理并不复杂。
现在可以把各模块流程图按图5-8所示的顺序组装成完整的流程图,如图5-9所示(sqrt(d)表示求d的算术平方根)。
根据流程图就可以用C语言编写程序了。
#include"stdio.h"
#include"math.h"
main()
{floata,b,c,d,a2,x1,x2;
printf("Inputa,b,c\n");
scanf("%f,%f,%f",&a,&b,&c);
if(a==0)/*解一元一次方程*/
x1=-c/b;
else/*解一元二次方程*/
{d=b*b-4*a*c;
a2=2*a;
x1=-b/a2;
if(d>=0)x2=sqrt(d)/a2;
elsex2=sqrt(-d)/a2;
}
if(a==0)/*输出一次方程根*/
printf("root=%f\n",x1);
else
if(d>=0)/*输出实根*/
{printf("realroot:
\n");
printf("r00t1=%f,r00t2=%f\n",x1+x2,x1-x2);
}
else/*输出复根*/
{printf("complexroot:
\n");
printf("root1=%f+%fi\n",x1,x2);
printf("root2=%f-%fi\n",x1,x2);
}
}
运行程序:
输入:
0,10,5↙
输出:
root=-0.500000
输入:
1,2,5↙
输出:
complexroot:
root1=-1.000000+2.000000i
root2=-1.000000-2.000000i
输入:
1,5,2↙
输出:
root1=-0.438447,root2=-4.561553
本章小结
根据某种条件的成立与否而采用不同的程序段进行处理的程序结构称为选择结构。
选择结构又可分为简单分支(两个分支)和多分支两种情况。
一般,采用if语句实现简单分支结构程序,用switch和break语句实现多分支结构程序。
虽然用嵌套if语句也能实现多分支结构程序,但用switch和break语句实现的多分支结构程序更简洁明了。
if语句的控制条件通常用关系表达式或逻辑表达式构造,也可以用一般表达式表示。
因为表达式的值非零为"真",零为"假"。
所以具有值的表达式均可作if语句的控制条件。
if语句有简单if和if_else两种形式,它们可以实现简单分支结构程序。
采用嵌套if语句还可以实现较为复杂的多分支结构程序。
在嵌套if语句中,一定要搞清楚else与哪个if结合的问题。
C语言规定,else与其前最近的同一复合语句的不带else的if结合。
书写嵌套if语句往往采用缩进的阶梯式写法,目的是便于看清else与if结合的逻辑关系,但这种写法并不能改变if语句的逻辑关系。
switch语句只有与break语句相结合,才能设计出正确的多分支结构程序。
break语句通常出现在switch语句或循环语句中,它能轻而易举地终止执行它所在的switch语句或循环语句。
虽然用switch语句和break语句实现的多分支结构程序可读性好,逻辑关系一目了然。
然而,使用switch(k)的困难在于其中的k表达式的构造。
goto语句可以方便快速地转到指定的任意位置继续执行(注意,goto语句与语句标号必须在同一函数中)。
正是它的任意性破坏了程序的自上而下的流程,可读性差,可维护性差,因而结构化程序设计中不提倡使用goto语句,甚至有人主张在程序设计语言中完全去掉goto语句。
然而,在某些场合适当使用goto语句能提高程序的效率。
习题5
一、单选题
5-1.if语句的控制条件。
A.只能用关系表达式B.只能用关系表达式或逻辑表达式
C.只能用逻辑表达式D.可以用任何表达式
5-2.以下程序的输出结果是。
main()
{floatx=2,y;
if(x<0)y=0;
elseif(x<5&&!
x)y=1/(x+2);
elseif(x<10)y=1/x;
elsey=10;
printf("%f\n",y);
}
A.0.000000B.0.250000C.0.500000D.10.000000
5-3.执行以下程序段后,a,b,c的值分别是。
inta,b=100,c,x=10,y=9;
a=(--x==y++)?
--x:
++y;
if(x<9)b=x++;c=y;
A.9,9,9B.8,8,10C.9,10,9D.1,11,10
5-4.执行下列程序段后,x、y和z的值分别是。
intx=10,y=20,z=30;
if(x>y)z=x;x=y;y=z;
A.10,20,30B.20,30,30C.20,30,10D.20,30,20
5-4.以下程序的输出结果是____。
main()
{intw=4,x=3,y=2,z=1;
if(x>y&&!
(z==w))