第3章 选择结构程序设计 选择结构要根据某个条件的满足与否,决定执行不同的操作。实现选择结构有if和 switch两种语句。 3.1 if语句 if语句有3种形式,即单分支if语句、双分支if语句和多分支if语句。 3.1.1 关系运算与单分支if语句 单分支if语句的一般形式是: if(表达式) 内嵌语句 其中,表达式可以是值为数值型的任何表达式,当然也可以是表达式的特殊形式———常 图3.1 单分支if语句执行过程 量、变量或函数。表达式值非0即为真,值为0就是假。 括号后的语句是if语句的内嵌语句,注意只能内嵌一个 语句。其执行过程见图3.1。 例如: if(x>y) max=x; 执行时先判断x是否大于y。若x>y,则将x的值赋给max,然后执行下一条语句;否则, 直接执行下一条语句。 语句中的x>y是一个关系表达式,即用关系运算符将两个运算对象连接起来的合法 的式子。关系运算符有6个:①<(小于);②<=(小于或等于);③>(大于);④>= (大于或等于);⑤==(等于);⑥!=(不等于)。 关系运算符都是双目运算符,左结合,其优先级高于赋值运算符,低于算术运算符。 前4种关系运算符<、<=、>和>=的优先级相同,后两种==和!=的优先级相同,而 前4种的优先级高于后两种。 关系表达式的值只有两个。关系表达式成立时,表示真,其值为1;关系表达式不成 立时,表示假,其值为0。例如,若a=3,b=2,c=1,则a>b的值为1,a+b==c的值 为0,b<1的值为0。 特别地,a>b>c的值是0,这是由于两个>优先级相同,结合方向为自左至右,故先 做a>b,其值为1,然后做1>c,值为0。所以,要表达a、b、c满足严格递降关系,即数学 意义上的a>b>c时,在C程序中直接写成a>b>c是错误的,应该使用3.1.3节介绍的 逻辑表达式。 关系运算 单分支if 语句 20 【例3-1】 输入一个数,若为正数则输出该数。 #include "stdio.h" main() { int a; scanf("%d",&a); if(a>0) printf("%d\n",a); /*如果a 是正数就输出a 的值*/ } 运行结果: 6 ↙ 6 再运行一次,其结果: -3 ↙ 【例3-2】 对输入的两个实数按由小到大的顺序输出。 #include "stdio.h" main() { float a,b; scanf("%f%f",&a,&b); if(a<=b) printf("%f,%f\n",a,b); /*如果a 比较小,就先输出a,后输出b*/ if(a>b) printf("%f,%f\n",b,a); /*如果b 比较小,就先输出b,后输出a*/ } 运行两次的结果: 1 2 ↙ 1.000000,2.000000 2 1 ↙ 1.000000,2.000000 若希望输出时一定按a、b的顺序,则在输出之前必须保证a中存放两者中较小的数, b中存放较大的数。如果输入的两数中a较大,则必须交换a、b的值。 如何实现呢? 可以这样想:a、b是两盒磁带,a中是歌曲,b中是英语,若希望交换这 两盒磁带的内容,必须再有一盒空白带t。可以先将磁带a中的歌曲转录到空白带t中 (t=a),然后将磁带b中的英语录到磁带a中(a=b),最后将磁带t中的歌曲录到磁带b 中(b=t),a、b的内容就互换了。也就是说,如果a>b,就执行3个语句t=a;a=b;b=t;。 但是,if语句只能内嵌一个语句,所以必须用{}把这3个语句括起来构成一个复合语句。 该方法程序如下。 #include "stdio.h" main() { float a,b,t; scanf("%f%f",&a,&b); 21 if(a>b) /*如果a>b,就交换a 和b 的值*/ { t=a; a=b; b=t; } printf("%f,%f\n",a,b); } 可以看到,内嵌语句从if(表达式)的下一行开始书写时,内嵌语句的各行都整齐地向 后缩进了若干列。建议采用此种缩格方式书写程序,既便于读懂程序,也便于查找错误。 程序中的if语句也可以改为 if(a>b) t=a,a=b,b=t; 其中的逗号不是分隔符,而是逗号运算符,用来把两个或多个表达式连接起来,组成一个 逗号表达式。 逗号表达式的一般形式是: 表达式1,表达式2,…,表达式n 逗号运算符也称顺序求值运算符,是双目运算符,优先级最低,结合方向是自左至右。 逗号表达式的运算过程是按从左到右的顺序逐个计算每一个表达式,即先计算表达式1, 再计算表达式2,……,最后计算表达式n。逗号表达式的值和类型是最后一个表达式的 值和类型。 在修改后的if语句中,由于使用了逗号运算符,括号后是一条语句,就不需要用{}把 它们括起来了。逗号运算符常用于此种情况,往往并不关心逗号表达式的值。 3.1.2 求余运算与双分支if语句 双分支if语句的一般形式是: if(表达式) 语句1 else 语句2 双分支if语句的执行过程见图3.2。双分支if语句是一条语句,它先计算表达式的 图3.2 双分支if语句执行过程 值,若表达式值为非0(即为真),则执行语句1;若表达式 值为0(即为假),则执行语句2。执行语句1或语句2后, 都直接执行if语句的下一条语句。 【例3-3】 输入一个整数,若为偶数则输出Yes,若为 奇数则输出No。 【分析】 判断一个整数的奇偶性,需使用求余运算 符%。a%b的结果是a除以b的余数。例如,5%2的值是1,8%2的值是0,2%5的值是2。 求余运算符也是基本算术运算符,优先级与乘法、除法运算符相同,结合方向为自左至右。 参与求余运算的两个量必须都是整型的。 双分支 if语句 22 对于一个整数a,可以根据a%2的值是否为0来判断其奇偶性。 #include "stdio.h" main() { int a; scanf("%d",&a); if(a%2==0) printf("Yes\n"); /*如果a 是偶数就输出Yes*/ else printf("No\n"); /*否则(a 是奇数)输出No*/ } 运行两次的结果: 5 ↙ No 12 ↙ Yes 【说明】 程序中的if语句可以改为 if(a%2) printf("No\n"); /*如果a 是奇数就输出No*/ else printf("Yes\n"); /*否则(a 是偶数)输出Yes*/ 如果a是奇数,则a%2的值为1,非0,将输出No;如果a是偶数,则a%2的值为0, 将输出Yes。 【注意】 求余运算的两个操作数必须都是整型的。 例3-2也可以用双分支if语句实现。 #include "stdio.h" main() { float a,b; scanf("%f%f",&a,&b); if(a<=b) printf("%f,%f\n",a,b); /*如果a 比较小,就先输出a,后输出b*/ else printf("%f,%f\n",b,a); /*否则(b 比较小)先输出b,后输出a*/ } 3.1.3 逻辑运算与多分支if语句 多分支if语句的一般形式是: if(表达式1) 语句1 else if(表达式2) 语句2 … else if(表达式m) 语句m [else 语句m+1] 其中的方括号表示其中内容可以缺省。 多分支 if语句 23 多分支if语句执行过程如图3.3所示。 图3.3 多分支if语句执行过程 多分支if语句是一条语句,它首先计算表达式1的值是否非0,非0即为真,就执行 语句1,整个if语句结束;否则计算表达式2的值是否非0,非0即为真,就执行语句2,整 个if语句结束;否则计算表达式3的值是否非0,……,如果所有m 个表达式的值都为0, 即都为假,有else就执行else后面的语句m+1,整个if语句结束;没有else则整个if语 句直接结束。 在有else的多分支if语句中,只有一个内嵌的语句会被执行到,即多个分支中只能 执行一个。不管执行了哪一个分支后都会直接执行多分支if语句的下一条语句。在无 else的多分支if语句中,多个分支可能执行一个,然后执行多分支if语句的下一条语句; 也可能一个也不执行,直接执行多分支if语句的下一条语句。 【注意】 else总是与它上面离它最近的尚未有else与之对应的if对应。else不能单 独使用,它只能出现在双分支或多分支if语句中,且必须有与之对应的if。 【例3-4】 函数y= -1, x<0 0, x=0 1, x>0 ì . í .. .. ,输入x的值后,请输出相应的y值。 【分析】 该分段函数的定义域覆盖了整个实数轴,故可用带有else的三分支if语句 来实现。 #include "stdio.h" main() { int x,y; scanf("%d",&x); if (x<0) y=-1; /*如果x<0,则y 赋值为-1*/ else if (x==0) y=0; /*否则如果x=0,则y 赋值为0*/ else y=1; /*否则(x>0)y 赋值为1*/ printf("%d\n",y); } 24 运行两次的结果: 3 ↙ 10 ↙ 0 【说明】 从分段函数的定义来看,x用浮点型比较合适,但y还应该是整型。读者可 以修改程序,把x用浮点型表示。 【注意】 判断两个数是否相等,必须使用关系运算符==。 【例3-5】 函数z= -1, x<0且y<0 0, x=0或y=0 1, x>0且y>0 ì . í .. .. ,输入x、y,输出相应的z值。 如何表达两者之间的“并且”“或者”关系呢? 这就要用到逻辑运算符。逻辑运算符有 3个:&&(逻辑与)、||(逻辑或)和!(逻辑非)。 逻辑与(&&)表示“并且”,当a和b都非0(为真)时,a&&b的值为1,否则值为0。 逻辑或(||)表示“或者”,a和b中只要有一个非0,a||b的值就是1;只有二者都是0时, a||b的值才是0。逻辑非(!)表示“非”,a非0时,!a的值是0;a为0时,!a的值是1。 !是单目运算符。单目运算符的优先级都相同,结合方向都是自右至左。!的优先级与 自增、自减运算符相同,高于基本算术运算符。&& 和||都是双目运算符,左结合,优先 级低于关系运算符,高于赋值运算符。&& 的优先级高于||。 用逻辑运算符连接的合法的式子称为逻辑表达式。逻辑表达式的值应该是一个逻辑 量真或假。 【注意】 C语言给出逻辑运算结果时,以数值1表示真,以0表示假;但判断一个量 是否为真时,以0表示假,以非0表示真。 若a=3,b=5,则!a值为0,a&&b值为1,a||b值为1,!a&&b值为0,a-b&&0 0 值为0。 学习了逻辑运算符和逻辑表达式后,就能够表达a、b、c满足严格递降关系,即数学意 义上的a>b>c了,例如,可以写作a>b&&b>c。也能编写出例3-5的程序了。 #include "stdio.h" main() { float x,y;int z; scanf("%f%f",&x,&y); if (x<0&&y<0) z=-1; /*如果x<0 且y<0,z 赋值为-1*/ else if (x==0||y==0) z=0; /*否则如果x=0 或y=0,z 赋值为0*/ else if (x>0&&y>0) z=1; /*否则如果x>0 且y>0,z 赋值为1*/ printf("%d\n",z); /*第8 行*/ } 运行3次的结果: -1 -5 ↙ 逻辑运算 25 -1 0 0 ↙ 01 -6 ↙ -858993460 【说明】 为什么最后一次的运行结果会是这样呢? 这是由于x的值是1、y的值是 -6,不在该分段函数的定义域内。执行if语句时,3个表达式的值都是0,于是没有执行 任何一个分支,就直接执行输出z的值了。由于在程序的执行过程中z没有被赋值,于是 就输出了一个不确定的值。 为了避免产生这种错误,可以只对定义域内的x、y输出z值,而对定义域外的x、y输 出数据错误的信息。例如,第8行语句可以改为 if (x*y>=0) printf("%d\n",z); /*如果x、y 不异号(在定义域内),就输出z 的值*/ else printf("数据错误\n"); /*否则输出“数据错误”*/ 【注意】 在一个变量没有得到确定的值之前,是不可以使用它的值的。 3.1.4 if语句的嵌套 if语句中内嵌了一个或多个if语句称为if语句的嵌套。例如: if() if() 语句1 else 语句2 }内嵌if 语句 else if() 语句3 内 嵌if 语句 也可以用if语句的嵌套编写出例3-4程序如下。 #include "stdio.h" main() { int x,y; scanf("%d",&x); if(x<=0) /*如果x≤0*/ if (x<0) y=-1; /*如果x<0,y 赋值为-1*/ else y=0; /*否则(x=0),y 赋值为0*/ else y=1; /*否则(x>0)y 赋值为1*/ printf("%d\n",y); } 3.1.5 条件运算符与条件表达式 条件运算符由?和:两个符号组成,是C语言中唯一的一个三目运算符,右结合,优先 if语句 的嵌套 条件运算 26 级低于逻辑运算符,高于赋值运算符。 条件表达式的一般形式是: 表达式1 ? 表达式2 : 表达式3 如果表达式1的值非0,则条件表达式的值是表达式2的值,否则条件表达式的值是 表达式3的值。 例如,max=(a>b)?a:b;相当于: if (a>b) max=a; else max=b; 3.1.6 程序举例 【例3-6】 有如下的分段函数,输入x的值后,请输出相应的y值。 y= 5 29|x-7|, x<-10 log316+cos32°, -10≤x<12.6 2x-πsinx ex2 , x≥12.6 ì . í ... ... 【分析】 该分段函数的定义域覆盖了整个实数轴,故可用带有else的三分支if语句 来实现。由分段函数的定义可知,x、y用浮点型比较恰当。由于程序中用到了数学函数, 故需要使用文件包含命令#include"math.h"。 #include "stdio.h" #include "math.h" main() { float x,y; scanf("%f",&x); if(x<-10) y=5.0/29*fabs(x-7); /*第6 行*/ else if(x<12.6) y=log(16)/log(3)+cos(32*3.14159/180); /*第7 行*/ else y=(sqrt(2*x)-3.14159*sin(x))/(exp(1)*x*x); /*第8行*/ printf("%f\n",y); } 运行3次的结果: -15.6 ↙ 3.896552 9 ↙ 3.371767 68.7 ↙ 0.001012 程序举例 27 【说明】 该程序中函数的定义域是(-∞,+∞),而且各分支是按照x从小到大的顺 序,所以程序若能执行到计算第二个表达式x<12.6的值,一定是第一个表达式x<-10 的值为假,即x≥-10,所以在第二个表达式中没有判断x是否大于或等于-10;同理,若 能执行到else子句,一定是前两个表达式的值均为假,即x≥12.6,所以就不用再进行判 断了。当然,在每个分支都进行所有判断也是可以的,即程序中的if语句(第6~8行)也 可以改为 if(x<-10) y=5.0/29*fabs(x-7); else if(x>=-10&&x<12.6) y=log(16)/log(3)+cos(32*3.14159/180); else if(x>=12.6) y=(sqrt(2*x)-3.14159*sin(x))/(exp(1)*x*x); 如果函数的定义域不是(-∞,+∞),或者各分支没有按照自变量从小到大或者从大 到小的顺序排,则必须在每个分支都进行所有判断。 该程序中涉及多个算术表达式。 【注意】 在书写算术表达式时, (1)乘号不能省。例如,2x必须写成2*x。 (2)分子、分母若是表达式,一般应将其用括号括起来,避免出错。例如,x+y 2a 应写作 (x+y)/(2*a)。 (3)两个整型量相除结果仍为整型,所以至少要将其中的一个转换为浮点型量。例 如,5 29可以写作5.0/29,系统会自动将29转换成浮点型29.0,然后再进行运算。 (4)函数的自变量必须用括号括起来。例如,sinx必须写作sin(x)。 (5)表达式中只可使用圆括号。可以多层嵌套使用,但左、右括号必须匹配。 (6)三角函数自变量的单位是弧度,若为角度必须转换为弧度。例如,cos32°可以写 作cos(32*3.14159/180)。 (7)若所用对数函数既不是自然对数也不是常用对数,则利用换底公式将其用自然 对数或常用对数来表示。例如,log316可以写作log(16)/log(3)或log10(16)/log10(3)。 (8)使用自然对数的底数e时必须写作exp(1),因为直接写作e系统会认为是一个 变量;π只能用它的某个近似值来表示,例如可以写作3.14159。 【例3-7】 有如下的分段函数,输入x的值后,请输出相应的y值。 y= x, 15≤x<30 50, 30≤x<100 2x-3, 100≤x<200 无意义, 其他 ì . í ... .. . 【分析】 该函数在定义域[15,200)内可以根据其所在位置得到相应的y值,但在定 义域外只能输出无意义。因此,应首先使用一个双分支if语句,区分开x是在定义域内 还是在定义域外。若x在定义域外,则直接输出“无意义”;若x在定义域内,则再使用一 个多分支if语句,根据x所在位置得到相应的y值,然后再输出该值。 28 #include "stdio.h" main() { float x,y; scanf("%f",&x); if(x>=15&&x<200) /*如果x 在定义域内*/ { if(x<30) y=x; / * 计 算 y 的值*/ else if(x<100) y=50; else y=2*x-3; printf("y=%.2f\n",y); /* 输 出 y 的 值*/ } else /*否则(x 不在定义域内)*/ printf("无意义\n"); /* 输 出 " 无 意义"*/ } 运行两次的结果: 62.5 ↙ y=50.00 3 ↙ 无意义 【说明】 当x在定义域内时,要执行两个语句,所以必须使用复合语句,即用{}将这 两个语句括起来。 3.2 switch语句 实现多分支也可以使用switch语句。 switch语句的一般形式是: switch(表达式) { case 常量1: 若干语句 case 常量2: 若干语句 … case 常量m: 若干语句 default: 若干语句 } 其中,表达式的值必须是整型或字符型。执行switch语句时首先计算switch后面括号中 表达式的值。若表达式的值与某个case后面常量的值相等,就从该case后面的语句开始 执行,直到switch语句的结束;如果表达式的值与所有case后面常量的值都不相等,就执 行default后面的语句。 default子句可以缺省。若表达式的值与所有case后面常量的值都不相等,而且没有 default子句,则switch语句就直接结束。 switch语句 29 【例3-8】 输入学生的成绩(分数),输出其对应的等级。优(90~100)、良(80~89)、 中(70~79)、及格(60~69)和不及格(0~59)分别用A、B、C、D、E表示。 【分析】 考虑从成绩的十位数容易得出其对应的等级,于是可以先做x/10,然后取 它的整数部分,9、10为优,8为良,7为中,6为及格,其余为不及格。 取一个实数的整数部分可以使用强制类型转换运算符实现。 强制类型转换的一般形式是: (类型标识符)(表达式) 其中(类型标识符)是强制类型转换运算符,作用是将其后面表达式的值转换为括号中指 定的类型。例如,浮点型变量a的值是3.6,则(int)a的值是3,(int)(a-1)的值是2, (float)(2*3)的值是6.0。强制类型转换运算符也是算术运算符,它是单目运算符,所以 优先级高于基本算术运算符,结合方向是右结合。 【注意】 强制类型转换只是转换表达式值的类型,对所涉及变量的类型和值没有 影响。例 如,浮点型变量a的值是3.6,则表达式(int)a的值是3,但运算后a的类型仍是浮 点型,值仍为3.6,就像做运算2*a后a的类型和值都不变一样。 利用强制类型转换运算符编写例3-8程序如下。 #include "stdio.h" main() { float x; int y; scanf("%f",&x); y=(int)(x/10); switch(y) { case 10: case 9: printf("A\n"); case 8: printf("B\n"); case 7: printf("C\n"); case 6: printf("D\n"); default: printf("E\n"); } } 运行结果: 86 ↙ BCDE 【说明】 为什么没有得到正确的结果呢? 这是由于switch后面括号中表达式y的值 30 是8,于是从case8后面的语句开始执行,直到switch语句结束,即从语句printf("B\n"); 开始执行,直到printf("E\n");结束。那么,如何在执行一个case分支后,终止switch语 句的执行呢? 可以使用break语句来达到此目的。修改后的正确程序如下: #include "stdio.h" main() { float x; int y; scanf("%f",&x); y=(int)(x/10); switch(y) { case 10: case 9: printf("A\n"); break; case 8: printf("B\n"); break; case 7: printf("C\n"); break; case 6: printf("D\n"); break; default: printf("E\n"); } } 运行结果: 86 ↙ B 【注意】 (1)case后面冒号之前的部分必须是常量或常量表达式,不能出现变量或函数。 (2)各case后面常量的值必须互不相同。 (3)多个case可以共用一组语句。如例3-8中的 case 10: case 9: printf("A\n");break; 本章小结 本章介绍了实现选择结构的if语句和switch语句,重点是if语句。if语句有3种形 式,分别是单分支if语句、双分支if语句和多分支if语句。 本章还介绍了多种运算符及其对应的表达式,并在3.1.6节中重点介绍了书写算术 表达式时的注意事项。 目前所介绍的运算符中,按优先级由高到低分别为: (1)括号()。 (2)单目运算符++、--、!、(类型标识符)。 (3)基本算术运算符*、/、%、+、-,其中*、/和%的优先级高于+、-。 31 (4)关系运算符>、>=、<、<=、==、!=,其中>、>=、<和<=的优先级高于 ==、!=。 (5)逻辑与&&。 (6)逻辑或||。 (7)条件运算符? :。 (8)赋值运算符=。 (9)逗号运算符。 单目运算符和唯一的三目运算符? :的结合方向都是自右至左,双目运算符中只有赋 值运算符的结合方向是自右至左,其余双目运算符的结合方向都是自左至右。 习题3 3-1 输入一个学生的成绩score,如果score≥60,则输出pass,否则输出fail。 3-2 输入两个数,输出其中较大的数。 3-3 输入3个数,对其按照由小到大的顺序输出。 3-4 输入3条边长a、b、c,如果它们能构成一个三角形就计算该三角形的面积,否则输出 不是三角形的信息。 3-5 有如下的分段函数,输入x的值后,请输出相应的y值。 y= log23+xsin66°, x<3 e, x≥3 { 3-6 有如下的分段函数,输入x的值后,请输出相应的y值。 y= 16 ex+sinx, x>1 2x+5, -1