第 5 章 选择结构 .. 5.1 概 述 在问题求解中语句的执行需要根据条件进行判断,并选择符合逻辑的语句或 者语句块来执行,具体表现形式为分支选择结构。分支选择结构的主要作用是在 顺序结构的基础上,根据条件判断结果改变代码执行顺序,使得程序能够根据不 同的情况做不同的响应。 常见的选择结构有单分支选择结构、双分支选择结构、多分支选择结构以及 嵌套分支选择结构,如图5.1所示。 图5.1 选择结构 .. 5.2 单分支选择结构 单分支选择结构语法: if(表达式){ 语句; } 如果表达式为真(非0),则执行语句,否则不执行。 说明: (1)表达式可以是任何类型,常用的是关系或逻辑表达式。 (2)语句可以是一条语句或者语句块,也可以是另一个if语句。 50 程序设计与问题求解(C语言版·微课版) 执行流程如图5.2所示。 图5.2 单分支选择结构 例5.1 输入一个整数,如果是偶数,则输出even。 判断一个整数n是否为偶数的方法是测试该整数是否为2的倍数,即n%2的结果是否 为0。根据这个结果进行相应输出,对不同的n进行不同的响应。 代码如下: #include <stdio.h> #include <stdlib.h> int main(){ int num; scanf("%d",&num); if(num %2 == 0){ printf("even"); } return 0; } 当if中表达式成立时,说明num 是偶数,此时输出结果。本例中仅执行一条输出语句, 可以不用{},但为提高代码的可读性,培养良好的编程规范,建议加上{}。 .. 5.3 双分支选择结构 当判断为假的分支也需要处理相应的逻辑时,可以使用双分支选择结构,相应的语句为 if-else语句,语法如下。 if(表达式){ 语句1; }else{ 语句2; } 第5章 选择结构 51 如果表达式为真(非0),则执行语句1,否则执行语句2。 图5.3 双分支选择结构 说明: (1)表达式可以是任何类型的,常用的是关系或逻 辑表达式。 (2)语句1和语句2可以是任何可执行语句,也可 以是另一个if-else语句。 执行流程如图5.3所示。 例5.2 输入一个字符,如果是数字字符则输出" digital",否则,输出"nondigital"。 对于一个字符ch,测试其ASCII是否为'0'~'9',如 果是则为数字字符,输出"digital",否则输出"non digital"。因为本例对于是或不是数字字符这两种情况 都需要有响应,所以需要使用if-else结构。 代码如下: #include <stdio.h> #include <stdlib.h> int main(){ char char; scanf("%c",&ch); if(c >= '0' && c <= '9'){ printf("digital"); }else{ printf("non digital"); } return 0; } 例5.3 输入年份,判断该年份是否为闰年。 判断年份year是否为闰年的表达式为: year % 4 == 0 && year % 100 != 0 || year % 400 == 0 因为表达式中逻辑与&& 的优先级高于逻辑或||的优先级,所以先计算year%4== 0&&year%100!=0子式,如果该子式成立,则不计算第二个子式year%400==0,否 则计算第二个子式。 为了提高程序的可读性,也可以通过()来明确子式的计算顺序: (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) 完整代码如下: #include <stdio.h> #include <stdlib.h> 52 程序设计与问题求解(C语言版·微课版) int main(){ int year; scanf("%d",&year); if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){ printf("%d is leap year."); }else{ printf("%d is not leap year"); } return 0; } 例5.4 再论鸡兔同笼。 鸡兔同笼需要求解方程组: x =4n -m 2 y =m -2n 2 ì . í .. .. 其中,n 为头的数量,m 为腿的数量。考虑到问题的特殊性,求解结果x、y 必须是整数解, 因此,首先要判断4n-m 、m -2n 是否能够被2整除,如果能够整除,则存在合法的解,否 则,问题不存在解。 改进后的代码如下: #include <stdio.h> #include <stdlib.h> int main(){ int n,m,x,y; scanf("%d %d",&n, &m); if((4 * n - m) % 2 == 0 && (m - 2 * n) % 2 == 0){ x = (4 * n - m) / 2; y = (m - 2 * n) / 2; printf("chickens:%d rabbits:%d\n",x,y); }else{ printf("No solution."); } return 0; } 测试1: 35 94 chickens:23 rabbits:12 测试2: 35 93 No solution. 第5章 选择结构 53 .. 5.4 多分支选择结构 5.4.1 elseif多分支选择结构 多分支选择结构通过elseif多分支选择语句实现。 elseif多分 支选择结构 语法: if(表达式1){ 语句1; }else if(表达式2){ 语句2; }else if(表达式3){ … }else if(表达式n - 1){ 语句n - 1; }else{ 语句n; } 这种有规则的多层嵌套,又称为if语句的elseif结构。最后一个else及其下面的语句 n也可以不存在。 注意: (1)选择结构是从上到下匹配的,一旦匹配上某个条件后,整个条件语句就结束了,即 使后面也能匹配上条件也不会再执行了。 (2)使用if-elseif后可以不写else。 执行流程如图5.4所示。 图5.4 多分支选择结构 54 程序设计与问题求解(C语言版·微课版) 例5.5 输入学生成绩,输出其等级。 代码如下: #include <stdio.h> #include <stdlib.h> int main(){ int grade; scanf("%d",&grade); if(grade >= 90){ printf("A"); }else if(grade >= 80){ printf("B"); }else if(grade >= 70){ printf("C"); }else if(grade >= 60){ printf("D"); }else{ printf("E"); } return 0; } 如果第一个判断成绩成立,则不执行后续的判断,否则,执行第二个判断。如果第二个 判断不成立,则执行第三个判断,直到执行最后的else。 如果成绩为95,则第一个判断成立,输出A,后续的判断都不执行;如果输入的成绩为 65,则直到elseif(grade>=60)时判断才成立,输出D。 switch多分 支选择结构 5.4.2 switch多分支选择结构 当分支较多时,使用elseif语句的形式比较复杂,可以使用switch语句来实现,具体语 法为: switch(整型表达式){ case 常量表达式1: 语句1; [break;] case 常量表达式2: 语句2; [break;] … case 常量表达式n-1: 语句n-1 [break;] default: 语句n [break;] } 第5章 选择结构 55 在计算整型表达式的值后,将得到的值与每个case后的常量表达式进行比较,当表达 式的值与某个常量表达式的值相等时,执行后面的语句,直到遇到break语句为止。若表达 式的值与所有case后的常量表达式均不相同时,则执行default对应的语句。 例5.6 输入学生成绩,输出其等级(用switch语句实现)。 switch语句中用于判断的case语句判断的是一个常量,而成绩等级是一个范围,因此, 在使用switch时要将一个成绩范围表示为一个常量。 对于一个给定的grade,执行grade=grade/10,将一个10分的区间压缩为一个常量。 如将60~69的成绩映射到6,70~79的成绩映射到7,80~89的成绩映射到8…… 具体代码如下: #include <stdio.h> #include <stdlib.h> int main(){ int grade; scanf("%d",&grade); grade /= 10; switch(grade){ case 10: case 9: printf("A"); break; case 8: printf("B"); break; case 7: printf("C"); break; case 6: printf("D"); break; case 5: case 4: case 3: case 2: case 1: case 0: printf("E"); break; } return 0; } 注意: (1)switch语句与if语句不同,switch仅能判断表达式的值是否等于指定的常量,而if 可以计算并判断各种表达式。 (2)switch语句后必须为整型表达式。 (3)switch中可以有任意多的case语句,case后必须为常量。 (4)default可以省略。 (5)case和default顺序可以颠倒。 例5.7 输入一个日期(含年、月、日),计算该日期是该年度中的第几天。 根据月份计算天数,其中1、3、5、7、8、10、12月的天数为31,4、6、9、11月的天数为30 天,2月份的天数需要根据年份判断,如果为闰年则为29天,否则为28天。 将指定月份之前所有月份的天数相加,然后加上本月的天数,如果月份大于2且为闰 年,天数加1。 56 程序设计与问题求解(C语言版·微课版) 完整代码如下: #include <stdio.h> #include <stdlib.h> int main(){ int year, month, day; scanf("%d-%d-%d",&year,&month,&day); //判断闰年 int isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); int days = 0; switch(month){ case 1: days = day; break; case 2: days = 31 + day; break; case 3: days = 31 + 28 + day; break; case 4: days = 31 + 28 + 31 + day; break; case 5: days = 31 + 28 + 31 + 30 + day; break; case 6: days = 31 + 28 + 31 + 30 + 31 + day; break; case 7: days = 31 + 28 + 31 + 30 + 31 + 30 + day; break; case 8: days = 31 + 28 + 31 + 30 + 31 + 30 + 31 + day; break; case 9: days = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + day; break; case 10: days = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + day; break; case 11: days = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + day; break; case 12: days = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + day; break; } if(month > 2 && isLeapYear){ days ++; } printf("%d-%d-%d is the %dth day of year",year,month,day,days); return 0; } 运行结果: 2020-12-31 2020-12-31 is the 366th day of year .. 5.5 嵌套分支选择结构 选择结构可以嵌套使用,即在判断正确的分支中执行的是一条选择语句。 例5.8 输入3个整数,输出最大的整数。 #include <stdio.h> #include <stdlib.h> int main(){ int x, y, z, max; 第5章 选择结构 57 scanf("%d,%d,%d",&x, &y, &z); if(x > y){ if(x > z) { max = x; }else{ max = z; } }else{ if(y > z){ max = y; }else{ max = z; } } printf("the max is : %d\n",max); return 0; } 注:C 语言规定了if和else的就近匹配原则,即else和最近的没有配对的if配对,与 书写格式无关。 在执行语句后面都加上{},匹配的ifelse保持相同的缩进,可以使逻辑更加清晰,且不 容易出错。 查看如下代码: int main(){ if(…) if(…) printf("…"); else printf("…"); return 0; } 代码虽然主观上将else与第一个if配对,但按照就近原则,else与第二个if配对,由此 造成了程序逻辑错误。因此,在编写代码时,尽量用{}界定范围,防止出现语法错误。 例5.6中虽然可以将百分制输出为等级制,但在边界上存在问题,即当输入-1~-9、 101~109时,程序仍然可以输出等级,因此,对于边界的成绩需要进行判断,只对合法的输 入进行等级输出,其他的成绩输出为“输入错误”,代码修改如下: #include <stdio.h> #include <stdlib.h> int main(){ int grade; scanf("%d",&grade); if(grade >= 0 && grade <= 100){ grade /= 10; switch(grade){ case 10: case 9: printf("A"); break; 58 程序设计与问题求解(C语言版·微课版) case 8: printf("B"); break; case 7: printf("C"); break; case 6: printf("D"); break; case 5: case 4: case 3: case 2: case 1: case 0: printf("E"); break; } }else{ printf("输入错误"); } return 0; } .. 5.6 条件表达式 条件运算符是C语言中唯一的三元运算符,需要3个运算对象,每个运算对象都是一 个表达式,如下所示: 表达式1 ? 表达式2: 表达式3 计算方法:如果表达式1为真,整个条件表达式的值是表达式2的值,否则,是表达式3 的值。因此,三目运算符实际上就是一个if-else结构。 例5.9 求3个整数中的最大值。 #include <stdio.h> #include <stdlib.h> int main(){ int x, y, z; scanf("%d,%d,%d",&x,&y,&z); int max = x > y ? ( x > z ? x : z) : (y > z ? y : z); printf("the max element is %d\n", max); return 0; } 使用嵌套的三目运算符虽然可以用少量的代码实现比较复杂的功能,但这将导致程序 的可读性大大降低,因此,不建议将三目运算符进行嵌套。 一元二次 方程求根 .. 5.7 能力拓展 5.7.1 一元二次方程求根 给定一元二次方程f(x)=ax2+bx+c,其中a、b、c 为实数,求方程的根(精确到小数 点后6位)。