第3章选择结构程序设计 选择结构是程序的3种逻辑结构之一,在C语言程序中使用if命令和switch命令实现选择结构。本章系统介绍选择结构程序设计知识,主要内容包括用于表示条件的关系表达式和逻辑表达式、if命令和switch命令的结构及执行过程、选择结构程序设计的基本方法等。 任何选择处理都是有条件的,合理、正确地表达和使用选择条件是选择结构程序设计的重要内容。 3.1if选择结构 在第1章关于选择结构算法的知识中讨论了判定“优等生”问题的选择结构算法(算法流程图见图15),其中分支选择的条件是ave≥90(ave表示平均成绩),该条件成立时显示“优等生”,否则显示“加油!”。本节从此算法的实现程序开始,逐步介绍if选择结构的相关知识。 3.1.1if选择结构程序示例 【例31】输入一个学生的两门课程的成绩,若平均成绩不低于90,则显示“优等生”,否则显示“加油!”。 程序如下: #include int main() { int s1,s2,ave;/*s1、s2为课程成绩,ave为平均成绩*/ printf("输入两门课程的成绩: "); scanf("%d,%d",&s1,&s2);   /*输入课程成绩s1、s2*/ ave=(s1+s2)/2;   /*计算平均成绩ave*/ if(ave>=90)   /*选择控制*/ printf("优等生\n");   /*ave不低于90时执行该语句*/ else printf("加油!\n");   /*ave不足90时执行该语句*/ return 0; } 程序解析: 该程序中的ifelse命令用于实现选择控制,选择条件是ave>=90。当ave>=90成立时执行语句“printf("优等生\n");”,输出字符串“优等生”; 否则执行语句“printf("加油!\n");”,输出字符串“加油!”。本例中决定选择哪个分支的条件ave>=90称为关系表达式。 以下是程序的执行实例,请读者根据具体数据分析程序的选择控制过程。 程序第1次执行结果: 输入两门课程的成绩: 88,96 (此时表达式ave>=90成立) 优等生 程序第2次执行结果: 输入两门课程的成绩: 77,85 (此时表达式ave>=90不成立) 加油! 3.1.2关系表达式 关系表达式是由关系运算符连接运算对象而构成的表达式。在选择结构中,进行分支选择的条件常使用关系表达式。例如,在例31的程序中使用ave>=90作为选择控制条件,其中的>=符号称为关系运算符。 1. 关系运算符 C语言有6种关系运算,分别表示两个对象进行大小比较的6种情况,即大于、大于或等于、小于、小于或等于、等于、不等于,其运算符及含义如表31所示。 表31关系运算符及其含义 关系运算符含义实例 >大于ave>90 >=大于或等于ave>=90 <小于ave<90 <=小于或等于ave<=90 ==等于ave==90 !=不等于ave!=90 2. 关系表达式的值 关系表达式只有两个取值,或者是1,或者是0。当关系表达式所表示的“关系”成立时,其值为1,否则其值为0。例如在例31中,第1次执行程序时变量ave的值为92,关系表达式ave>=90成立,则其值为1; 第2次执行程序时变量ave的值为81,关系表达式ave>=90不成立,则其值为0。 3. 关系运算的优先级 (1) 关系运算>、>=、<以及<=的优先级相同,关系运算==和!=的优先级相同,前面一组运算符的优先级高于后面一组运算符的优先级。 (2) 关系运算的优先级低于算术运算的优先级。 (3) 关系运算的优先级高于赋值运算的优先级。 4. 关系运算的结合性 6种关系运算都是左结合的。例如,关系表达式a<5>b与(a<5)>b等价,若a=-2、b=2,则其值为0。 3.1.3逻辑表达式 逻辑表达式是由逻辑运算符连接运算对象而构成的表达式,它在程序中常用于表示复杂条件。例如上述“优等生”问题,如果要求当每门课程的成绩都不低于90时判定为“优等生”,那么程序中判断是不是优等生的条件就要发生变化,满足优等生的条件是关系表达式s1>=90与s2>=90都要成立。以下是该复杂条件的逻辑表达式,其中&&称为逻辑与运算。 s1>=90&&s2>=90 1. 逻辑运算符 C语言的逻辑运算符有3个,分别为逻辑与运算符“&&”、逻辑或运算符“||”以及逻辑非运算符“!”。 当两个条件为“并且”关系时,使用与运算“&&”表示。实例如上。 当两个条件为“或者”关系时,使用或运算“||”表示。例如关于上述优等生问题,若有任一门课程的成绩不低于90,即判定为“优等生”,则判定“优等生”的逻辑表达式如下。 s1>=90||s2>=90 当对某个条件进行否定时使用非运算“!”表示。例如,表达式s1>=90可用如下“!”运算表示。 !(s1<90) 2. 逻辑表达式的值 逻辑表达式只有1和0两个取值。当逻辑表达式所表示的条件成立时(条件为“真”),其值为1; 否则(条件为“假”)其值为0。设a、b为表示条件的表达式,则对应于a、b的各种取值时,逻辑表达式!a、a&&b和a||b的结果值如表32所示。该表称为逻辑运算真值表,它清楚地描述了逻辑运算&&、||、!的功能。 表32逻辑运算真值表 ab!aa&&ba||b 1(真)1(真) 1(真)0(假)0(假) 1(真)1(真) 0(假)1(真) 0(假)1(真) 0(假)0(假)1(真) 0(假)1(真) 0(假)0(假) 【例32】将数学关系式2020而且x≤100,使用逻辑与运算&&可描述x的取值关系如下: x>20&&x<=100 (2) 逻辑表达式求值。 当x=50时,表达式x>20为1,表达式x<=100为1,则逻辑表达式x>20&&x<=100的值为1。 3. 逻辑运算符的优先级和结合性 (1) 优先级: “!”高于“&&”,“&&”高于“||”。 (2) 优先级: “!”高于算术运算符,“&&”“||”低于关系运算符。 (3) 结合性: “&&”“||”是左结合的,“!”是右结合的。 4. 逻辑运算的特点 (1) 由与运算“&&”构成的逻辑表达式自左至右求值,若运算符“&&”左侧的表达式的值为0,则其右侧表达式不再运算,整个与运算表达式的值为0。例如对逻辑表达式a&&b求值,若a为0,则表达式的值为0,对b不再求值; 若a为非0,则要计算b的值。 (2) 由或运算“||”构成的逻辑表达式自左至右求值,若运算符“||”左侧的表达式的值为1,则其右侧表达式不再运算,整个或运算表达式的值为1。例如对逻辑表达式a||b求值,若a为1,则表达式的值为1,对b不再求值; 若a为0,则要计算b的值。 3.1.4if命令 if命令是最基本的分支控制命令,在具体应用中有多种不同的使用形式,但不管何种形式,都要首先判断给定的条件,然后决定下一步要执行程序的哪些语句。 1. 双分支if命令 双分支if命令的一般形式如下: if(表达式) {语句组1} else {语句组2} 其中,“表达式”是if命令进行分支处理的条件; “语句组”包含若干和C语句,当它只有一条语句时大括号“{}”可以省略。 图31双分支if命令流程图 双分支if命令的执行过程如下: 首先对“表达式”求值,然后进行分支判断。若“表达式”为非0值(即条件成立),则执行{语句组1},然后执行紧接{语句组2}之后的语句; 否则(“表达式”的值为0,即条件不成立)执行{语句组2},然后继续向下执行其他语句。双分支if命令的流程图如图31所示。 【例33】计算下面分段函数的值(关系表达式作为选择条件)。 y=x+25(x>0) x-25(x≤0) 1) 问题分析与算法设计 该分段函数y有两个计算分支,具体由x的值确定。若x>0,则y=x+25; 否则y=x-25。当然,也可以使用x≤0作为选择条件,即若x≤0,则y=x-25; 否则y=x+25。本例使用x>0作为选择条件,算法流程图如图32所示。 图32例33算法流程图 2) 实现程序 程序如下: #include int main() { int x,y; printf("x="); scanf("%d",&x);   /*输入x的值*/ if(x>0)   /*判断x>0是否成立*/ y=x+25;   /*x>0成立时*/ else y=x-25;   /*x>0不成立时*/ printf("y=%d\n",y);   /*输出y的值*/ return 0; } 3) 程序执行实例 下面是程序的执行实例。 第1次执行结果: X=30 (输入30,然后按Enter键) Y=55 本次执行时,if命令中“表达式”x>0的值为1,因此,执行语句“y=x+25;”,if命令即执行结束,另一个分支的语句“y=x-25;”不被执行。 第2次执行结果: X=-30 (输入-30,然后按Enter键) Y=-55 本次执行时,if命令中“表达式”x>0的值为0,因此,执行语句“y=x-25;”,if命令即执行结束,另一个分支的语句“y=x+25;”不被执行。 if命令执行结束后即执行其后的语句“printf("Y=%d\n",y);”,输出计算结果。 【例34】输入一个学生的两门课程的成绩,若每门课程的成绩都不低于90,则显示“优等生”,否则显示“加油!”(逻辑表达式作为选择条件)。 本例是对if选择结构的示例程序例31的拓展,实现分支选择的条件由一个关系表达式拓展为一个逻辑表达式。若课程成绩仍由s1和s2存储,则可使用逻辑表达式s1>=90&&s2>=90表示选择判断的条件。 程序如下: #include int main() { int s1,s2; printf("输入课程成绩: "); scanf("%d,%d",&s1,&s2); if(s1>=90&&s2>=90) printf("优等生\n");   /*每门课程的成绩都不低于90*/ else printf("加油!\n");   /*至少有一门课程的成绩不足90*/ return 0; } 在该程序中,逻辑表达式s1>=90&&s2>=90作为if命令的选择条件,当该条件成立(逻辑表达式的值为1)时显示“优等生”,否则(逻辑表达式的值为0)显示“加油”。程序的算法流程图如图33所示。 图33例34算法流程图 以下是程序的执行实例。 第1次执行结果: 输入课程成绩: 88,96 加油! 本次执行时,if命令中“表达式”s1>=90&&s2>=90的值为0,因此,执行语句“printf("加油!\n");”,输出“加油!”,if命令即执行结束,另一个分支的语句“printf("优等生\n");”不被执行。 第2次执行结果: 输入课程成绩: 95,90 优等生 本次执行时,if命令中“表达式”s1>=90&&s2>=90的值为1,因此,执行语句“printf("优等生\n");”,输出“优等生”,if命令即执行结束,另一个分支的语句“printf("加油!\n");”不被执行。 【例35】从键盘输入一个字符,若其为大写英文字母,则在屏幕上输出它的小写形式,否则原样输出该字符。 1) 问题分析 (1) 输入字符后首先要判断它是否为大写字母。设输入量为ch,则下面的逻辑表达式成立(其值为1)时ch为大写字母: ch>='A'&&ch<='Z' (2) 当ch为大写字母时,其对应的小写字母的ASCII码值为ch+32,以%c格式输出该表达式,即显示为小写字母。 2) 算法设计 (1) 输入字符并存储到ch中。 (2) 判断逻辑表达式ch>='A'&&ch<='Z',若其成立,则以%c格式输出ch+32,否则输出ch。 3) 实现程序 程序如下: #include int main() { char ch; printf("Input: "); ch=getchar();   /*输入字符*/ printf("Output: "); if(ch>='A'&&ch<='Z')   /*判断ch是不是大写字母*/ printf("%c\n",ch+32);  /*ch为大写字母时输出其小写形式*/ else printf("%c\n",ch);   /*ch不是大写字母时原样输出*/ return 0; } 4) 程序执行实例 第1次执行结果: Input: T Output: t 本次执行时,ch获得字符T,if命令中“表达式”ch>='A'&&ch<='Z'的值为1,因此,执行语句“printf("%c\n",ch+32);”,输出T的小写形式t,if命令即执行结束,程序执行结束。 第2次执行结果: Input:example Output:e 本次执行时,ch获得字符e,if命令中“表达式”ch>='A'&&ch<='Z'的值为0,因此,执行语句“printf("%c\n",ch);”,输出e,if命令即执行结束,程序执行结束。 问题思考 在上面的运行结果中,当输入字符串example时输出结果为e。为什么在输入一个字符串时只输出一个字符? 2. 单分支if命令 单分支if命令是双分支if命令的一种简化结构,其一般形式如下: if(表达式) {语句组} 其中,“表达式”和“语句组”的含义与在双分支if命令中的含义相同。 单分支if命令被执行后,首先对“表达式”求值,若表达式的值为非0(即条件成立),则执行{语句组},然后继续向后执行其他语句; 否则不执行{语句组},而直接执行{语句组}之后的语句。简而言之,该if命令的功能是根据条件表达式的值决定是否执行{语句组},其控制过程的流程图如图34所示。 【例36】输入一个学生的两门课程的成绩,若平均成绩不低于90,则显示“优等生”。 1) 算法设计 这是例31的一个简化问题,算法流程图如图35所示。 图34单分支if命令流程图 图35例36算法流程图 2) 实现程序 程序如下: #include int main() { int s1,s2,ave; printf("输入两门课程的成绩: "); scanf("%d,%d",&s1,&s2);   /*输入课程成绩s1、s2*/ ave=(s1+s2)/2;   /*计算平均成绩ave*/ if(ave>=90)   /*判断ave>=90是否成立*/ printf("优等生\n");   /*ave>=90成立时执行该语句*/ return 0; } 3) 程序执行实例 执行程序时,若输入的成绩数据满足优等生的条件,则显示“优等生”,否则立即结束,不会显示任何信息。以下为程序执行实例。 第1次执行结果: 输入两门课程的成绩: 90,96 优等生 本次执行时,if命令中“表达式”ave>=90的值为1,因此,执行语句“printf("优等生\n");”,输出“优等生”,if命令即执行结束,程序执行结束。 第2次执行结果: 输入两门课程的成绩: 90,80 本次执行时,if命令中“表达式”ave>=90的值为0,因此,语句“printf("优等生\n");”不会被执行,if命令即执行结束,程序执行结束。 3. if命令的嵌套结构 当一个if命令的{语句组}内又使用了if命令时就形成了if命令的嵌套结构,这种结构用于多重条件判断的情况。图36是if命令嵌套结构示意图,该图只表示了嵌套的两种情况。 图36if命令嵌套结构示意图 【例37】输入一个学生的两门课程的成绩。若平均成绩小于0,则显示“数据错误!”; 否则,若平均成绩不低于90,则显示“优等生”,低于90则显示“加油!”。 1) 算法设计 根据平均成绩的计算结果,问题处理将有以下两个大的分支。 分支一: 平均成绩小于0,显示“数据错误!”; 分支二: 平均成绩不小于0,进一步进行小分支处理。 算法流程图如图37所示。下面对照流程图,先给出具体的实现程序,然后对if命令的嵌套情况进行说明。希望读者能够借助程序流程图加深对if命令嵌套结构的认识。 图37if命令的嵌套结构示例 2) 实现程序 程序如下。其中有些程序行上加了编号,这些编号不属于源程序的内容,只是为了便于在后续讲解中说明程序的嵌套结构而添加的标记。 #include int main() { int s1,s2,ave; printf("输入两门课程的成绩: "); scanf("%d,%d",&s1,&s2);   /*输入两门课程的成绩*/ ave=(s1+s2)/2;   /*计算平均成绩,存储到变量ave中*/ ①if(ave<0)   /*外层if*/ ②printf("数据错误!\n"); ③else   /*与外层if配对*/ ④if(ave>=90)   /*内层if*/ ⑤printf("优等生\n"); ⑥else   /*与内层if配对*/ ⑦printf("加油!\n"); return 0; } 在该程序中有两个if命令,①和③构成外层的if命令,②位于外层if命令的{语句组1}内,④、⑤、⑥、⑦位于外层if命令的{语句组2}内,④和⑥构成的if命令嵌套在外层if命令中。 3) 程序执行实例 第1次执行结果: 输入两门课程的成绩: 70,-90 数据错误! 本次执行时,①行if命令中“表达式”ave<0的值为1,因此,执行②行的语句“printf("数据错误!\n");”,输出“数据错误!”,if命令即执行结束,程序执行结束。 第2次执行结果: 输入两门课程的成绩: 90,95 优等生 本次执行时,①行if命令中“表达式”ave<0的值为0,因此,执行其所属的else分支的语句组,④行的if命令被执行,此时其“表达式”ave>=90的值为1,则⑤行的语句“printf("优等生\n");”被执行,输出“优等生”,if命令即执行结束,程序执行结束。 第3次执行结果: 输入两门课程的成绩: 70,65 加油! 本次执行时,①行if命令中“表达式”ave<0的值为0,因此,执行其所属的else分支的语句组,④行的if命令被执行,此时其“表达式”ave>=90的值为0,则⑦行的语句“printf("加油!\n");”被执行,输出“加油!”,if命令即执行结束,程序执行结束。 使用嵌套的if命令首先应避免出现嵌套混乱。C语言规定,在else语句无明确配对结构时,else与其前最近的一个尚未配对的if配对。下面是两个程序段,可以进一步说明else和if的配对情况。 程序段一: if(x>20) { if(y>100) printf("Good"); } else printf("Bad"); 程序段二: if(x>20) if(y>100) printf("Good"); else printf("Bad"); 程序段一整体上是一个ifelse的结构,else与第1个if配对。程序段二整体上是一个单分支if语句,else与第2个if配对,共同作为第1个if命令的语句体。对于这两个程序段的执行结果,请读者分析并进行验证。 4. ifelse if结构 ifelse if结构属于ifelse结构的嵌套形式,适用于多分支的选择控制。它的一般形式如下: if(表达式1) {语句组1} else if(表达式2) {语句组2} else if(表达式3) {语句组3}  else if(表达式n) {语句组n} else {语句组n+1} 该结构中的每个“表达式”都是进行分支选择的条件,“表达式”的值为非0值时表示选择条件成立,所属的语句组即被执行。 以下是ifelse if命令的具体执行过程: 当ifelse if命令被执行后,即从上到下逐个对条件进行判断, 一旦条件成立就执行该条件所属的语句组,其下的所有条件都不再判断,它们的语句组也不被执行,ifelse if命令即执行结束; 当任何一个条件都不成立时,则执行{语句组n+1},并结束ifelse if命令。图38所示为四层if结构的ifelse if语句流程图。 图38ifelse if结构的流程图 【例38】用ifelse if结构改写例37的程序。 程序如下: #include int main() { int s1,s2,ave; printf("输入两门课程的成绩: "); scanf("%d,%d",&s1,&s2); ave=(s1+s2)/2; if(ave<0) printf("数据错误!\n");   /*ave<0成立时*/ else if(ave>=90) printf("优等生\n");   /*ave<0不成立,而ave>=90成立时*/ else printf("加油!\n");   /*ave<0不成立,ave>=90也不成立时*/ return 0; } 请读者对照例37的程序,通过具体数据分析该程序的执行过程。 3.1.5条件运算 条件运算是C语言中唯一的一个三目运算,运算符由“?”和“:”构成,它根据条件从两个表达式中选择一个进行计算。有些简单的ifelse结构可通过条件运算实现。 1. 条件运算表达式 由条件运算符构成的表达式称为条件运算表达式,其一般形式如下: 表达式1?表达式2:表达式3 例如: 5?19+6:21 条件运算表达式按以下过程求值: (1) 计算“表达式1”的值; (2) 当“表达式1”的值为非0时,取“表达式2”的值为条件运算表达式的值,否则取“表达式3”的值为条件运算表达式的值。 由此可以求得条件运算表达式5?19+6:21的值为25。 2. 条件运算的优先级和结合性 条件运算的优先级高于赋值运算,而低于关系运算。 【例39】用条件运算计算下面分段函数的值。 y=x+25(x>0) x-25(x≤0) 1) 问题分析 使用x>0作为计算y值的条件,即可得到计算y值的条件运算表达式。 y=(x>0)?(x+25):(x-25) 或 y=x>0?x+25:x-25 当然,也可以使用x≤0作为计算y值的条件,具体表达式请读者分析给出。 2) 算法设计 (1) 输入x; (2) 计算表达式(x>0)?(x+25):(x-25)的值,并存储在变量y中; (3) 输出y。 3) 实现程序 程序如下: #include int main() { int x,y; printf("x="); scanf("%d",&x);   /*输入x*/ y=(x>0)?(x+25):(x-25);   /*计算(x>0)?(x+25):(x-25),存储在y中*/ printf("y=%d\n",y);   /*输出y*/ return 0; } 在例33中,该分段函数的求值是通过if命令判断来实现的,请读者注意比较这两个程序的异同。 3.2switch选择结构 switch选择结构是多分支选择的常见形式,由switch命令实现,具有结构清晰的特点。 switch命令的一般形式如下: switch(表达式) { case 常量1: 语句组1 case 常量2: 语句组2  case 常量n: 语句组n default: 语句组n+1 } switch命令的执行过程如下: 首先计算表达式的值,然后从第1个case子句开始,由上至下依次扫描case子句,比较case子句中的常量值与表达式的值是否相同,一旦遇到相同的情况,即停止扫描过程,并从该case子句的语句组开始,依次向下执行各语句组,直至遇到强制中断命令break或执行完最后一个语句组为止。当所有case都不符合要求时执行default下的语句组。 在扫描case子句时,case的语句组不起任何作用; 开始执行语句后,其下的所有case子句以及default子句都被忽略掉,不再有任何作用。图39所示为switch命令执行过程的示意图。 图39switch命令执行过程示意图 使用switch命令时允许省略“default:”及其语句组。 【例310】switch执行过程示例程序。 程序如下: #include int main() { int i; scanf("%d",&i);   /*为变量i输入数据*/ switch(i) { case 0: printf("zero ");   /*i为0时输出*/ case 1: printf("one ");   /*i为0或1时输出*/ break;   /*终止switch语句*/ case 2: printf("two ");   /*i为2时输出*/ case 3: printf("three ");   /*i为2或3时输出*/ case 4: printf("four ");   /*i为2、3或4时输出*/ break;   /*终止switch语句*/ default: printf("other ");   /*i不为0、1、2、3、4时输出*/ } printf("\n"); return 0; } 分别用0、1、3、6为i提供数据,执行4次程序,结果如下: i=0时,输出zero one; i=1时,输出one; i=3时,输出three four; i=6时,输出other。 程序中的break为中断命令,其功能是终止switch命令,使程序立即执行switch命令的后续语句,即“printf("\n");”语句。 下面是关于switch命令的其他几点说明。 (1) 任何一个case的语句组允许为空。 当某个case的语句组为空时,表示它与下面的case执行相同的语句组。 下面是一个示例程序。 【例311】省略case语句组的示例程序。 程序如下: #include int main() { char result; scanf ("%c",&result);   /*为变量result输入字符*/ switch(result) { case 'A': case 'B': case 'C': printf ("Good!\n");   /*result为A、B或C时执行该语句*/ break;   /*终止switch语句*/ case 'D': case 'E': printf ("Bad!\n");   /*result为D或E时执行该语句*/ break;   /*终止switch语句*/ default: printf ("Error!\n");  /*result不是A、B、C、D、E时执行该语句*/ } return 0; } 该程序执行后,输入A、B或C时显示"Good!"; 输入D或E时显示"Bad!"; 其他输入则显示"Error!"。 (2) switch命令的“表达式”通常为整数型值或字符型值,case中常量的类型应与之相应。 (3) case中的“常量”允许是常数表达式,但不允许是变量表达式。 以下用法是正确的: case 5+8: case 'a'+6: 以下用法是错误的: case a+5: (4) switch命令允许嵌套,即在case的语句组中允许使用switch命令。 3.3选择结构程序实例 【例312】编写一个程序,根据公历历法的闰年规律判定某个年份是不是闰年。 1) 问题分析与算法设计 闰年是为了弥补因人为历法造成的年度天数与地球实际公转周期的时间差而设立的,补上时间差的年份即为闰年。地球绕太阳运行的周期约为365.24219天,即一回归年。公历的平年只有365天,比回归年约少0.24219天,每四年累计约少一天,把这一天加于2月末(2月29日),使当年延长为366天,这一年就为闰年。 按照每四年一个闰年计算,平均每年就要多算出0.0078天,经过四百年就会多出大约3天,因此每四百年中要减少3个闰年。所以规定公历年份是整百的,必须是400的倍数才是闰年,不是400的倍数的就是平年。例如,1700年、1800年和1900年为平年,2000年为闰年。 简而言之,公历历法中闰年遵循的规律为: 四年一闰,百年不闰,四百年再闰。 (1) 设用变量year表示年份,写出满足闰年条件的逻辑表达式。 当year是400的整数倍时为闰年,条件表示为: year%400==0 当year是4的整数倍,但不是100的整数倍时为闰年,条件表示为: year%4==0&&year%100!=0 对于年份year,满足上述任何一个条件均为闰年。因此,满足闰年条件的逻辑表达式如下: year%400==0||year%4==0&&year%100!=0 当上述表达式成立时年份year即为闰年。 (2) 输入year,根据上述逻辑表达式的值即可得到year是否为闰年的结论。 其算法流程图如图310所示。 图310闰年问题算法流程图 2) 实现程序 程序如下: #include int main() { int year;   /*定义存储年份值的变量*/ printf("Input year: "); scanf("%d",&year);   /*输入年份值*/ if(year%400==0||year%4==0&&year%100!=0)  /*判断是不是闰年*/ printf("%d,Yes!\n",year);   /*是闰年*/ else printf("%d,No!\n",year);   /*不是闰年*/ return 0; } 3) 程序执行实例 第1次执行结果: Input year: 2000 2000 ,Yes! 本次执行时,year的值是2000,闰年条件year%400==0成立。 第2次执行结果: Input year: 2008 2008 ,Yes! 本次执行时,year的值是2008,闰年条件year%4==0&&year%100!=0成立。 第3次执行结果: Input year: 2100 2100,No! 本次执行时,year的值是2100,闰年条件year%400==0||year%4==0&&year%100!=0不成立。 该程序主要是学习在if语句中使用综合条件的方法,将多种条件组合成逻辑表达式,可以简化程序设计。 当然,闰年条件year%400==0与year%4==0&&year%100!=0也可以分开使用,单独进行判断,以下是一个示例程序。 #include int main() { int year;   /* 定义存储年份值的变量 */ printf("Input year: "); scanf("%d",&year);   /* 输入年份值 */ if(year%400==0)   /* year能被400整除 */ printf("%d,Yes!\n",year); else if(year%4==0&&year%100!=0)   /* year能被4整除但不能被100整除 */ printf("%d,Yes!\n",year); else printf("%d,No!\n",year); return 0; } 请读者学习时注意比较以上两个程序的异同。 问题思考 下面的程序也能对闰年年份进行判断,功能与上面的程序相同,但程序更简洁,它将printf()函数和条件运算结合起来,巧妙地解决了闰年判断问题。请读者运行程序,并结合程序执行结果对程序进行解读分析。 #include int main() { int year; printf("year="); scanf("%d",&year); printf("%s\n",year%(year%100?4:400)?"NO":"YES"); return 0; } 【例313】设计求解一元二次方程ax2+bx+c=0(a≠0)的通用程序,并运行程序,对下面的两个方程求解。 方程一: 3x2+9x-1=0 方程二: 2x2-4x+3=0 1) 问题分析与算法设计 (1) 一元二次方程若有实根,则计算并输出实根x1,2=-b±b2-4ac2a,否则输出无实根信息。 (2) 程序的输入量为方程的系数a、b、c。输入不同的系数,则求解不同的方程。 (3) 程序中要使用数学函数sqrt(),因此注意包含math.h文件。 程序的算法流程图如图311所示。 2) 实现程序 程序如下: #include #include int main() { float a,b,c;   /*定义存储方程系数的变量*/ float x1,x2,d;   /*x1、x2存储方程根,d存储判别式的值*/ printf("Input a,b,c: "); scanf("%f,%f,%f",&a,&b,&c);  /*输入方程的系数值*/ d=b*b-4.0*a*c;   /*计算判别式的值*/ if(d>=0.0 && a!=0.0)   /*当方程有实根时求方程的两个实根*/ { x1=(-b+sqrt(d))/(2.0*a);   /*计算x1*/ x2=(-b-sqrt(d))/(2.0*a);   /*计算x2*/ printf("x1=%f,x2=%f\n",x1,x2);   /*输出x1、x2*/ } else   /*当方程无实根时输出无实根信息*/ printf("No real root or a is 0.\n"); return 0; } 图311求解一元二次方程的算法流程图 下面是求解方程一的执行结果: Input a,b,c: 3,9,-1 (输入方程一的系数) x1=0.107275,x2=-3.107175 下面是求解方程二的执行结果: Input a,b,c: 2,-4,3 (输入方程二的系数) No real root or a is 0. 【例314】学生成绩分等级显示。某班学生上两门课程,按百分制成绩(无小数位)进行考核。要求输入一个学生的两门课程的成绩,然后按平均成绩分等级显示考核结果。考核结果的等级标准如下。 优秀(excellence): 平均成绩≥90; 良好(all right): 80≤平均成绩<90; 中等(middling): 70≤平均成绩<80; 及格(pass): 60≤平均成绩<70; 不及格(fail): 平均成绩<60。 1) 问题分析与算法设计 实现学生成绩分等级显示的基本处理过程可以分成两个阶段。 (1) 输入成绩并计算平均成绩。 (2) 分等级显示。成绩分为5个等级,每个等级对应不同的条件。类似的多分支问题通常使用ifelse if结构或者switch结构进行逻辑控制。本例使用ifelse if结构,算法流程图如图312所示。 图312“学生成绩分等级显示”算法流程图 2) 实现程序 程序如下: #include int main() { int s1,s2,ave;   /*s1、s2为课程成绩,ave为平均成绩*/ printf("Score: "); scanf("%d,%d",&s1,&s2);   /*输入课程成绩*/ ave=(s1+s2)/2;   /*计算平均成绩*/ if(ave>=90) printf("Result: excellence\n");   /*优秀*/ else if(ave>=80) printf("Result: all right\n");   /*良好*/ else if(ave>=70) printf("Result: middling\n");   /*中等*/ else if(ave>=60) printf("Result: pass\n");   /*及格*/ else printf("Result: fail\n");   /*不及格*/ return 0; } 程序执行结果: Score: 77,98 (此时ave为87) Result: all right Score: 89,92 (此时,ave为90) Result: excellence 问题思考 (1) 对输入数据进行合法性检查是算法评价的一项重要指标(算法的健壮性)。上面的程序未检查输入数据的合法性,即使输入了百分制以外的成绩值(例如900、-90),也会进行等级判定,这显然是不合理的。请读者完善程序,使得只有输入数据合法时才会进行等级判定,输出相应结果。 (2) “学生成绩分等级显示”是一个多分支处理问题,上面的程序使用ifelse if结构实现分支控制。类似的多分支处理问题使用switch结构也能方便地实现。请读者使用switch结构改写上面的程序。 小结 本章介绍了选择结构知识,主要有选择结构控制命令if和switch,用于条件表达的关系表达式和逻辑表达式,以及选择结构程序的典型实例。 (1) if命令是最基本的选择结构控制命令,它有多种形式,通常用关系表达式和逻辑表达式表示选择控制条件。任何一种if命令的语句体中都可以出现其他if结构,这种结构称为if命令的嵌套结构。 (2) switch命令专门用于多路分支控制,适用于ifelse if形式的结构,而且更清晰。程序总是试图从满足条件的第1个case子句开始执行其后的所有语句,而不再对其后的case进行判断,因此必要时应使用break命令中断switch命令的运行。 (3) 本章最后通过闰年判定问题、求解一元二次方程和学生成绩分等级显示几个典型实例详细介绍了选择结构程序设计的方法和过程。 习题三 一、 选择题 1. 下面是由if构成的一个程序段: if(ab)c=a; a=b; b=c; 执行该程序段后变量a、b、c的值分别是。 A. 1、2、3B. 2、3、3 C. 2、3、1D. 2、3、2 4. 执行下面的程序段后a和b的值分别为。 int a=3,b=5,c; c=(a>--b)?a++:b--; A. 3、2B. 3、3C. 4、4D. 4、5 5. 以下程序段的输出结果是。 int x,y,temp; x=1,y=2; if(1) { if(xy) { if(z>x) max=z; } else if(y>x) max=y; printf("max=%d\n",max); 执行该程序段后的输出结果为。 A. max=1B. max=2 C. max=3D. 不确定 7. 有变量定义如下: char ch='A'; 则下列表达式的值是。 ch=(ch>'A'&& ch<='Z')?(ch+32):ch; A. AB. aC. ZD. z 8. 有程序段如下: int year=2000; printf("%s\n",year%(year%100?4:400)?"NO":"YES"); 执行上面的printf()语句后输出结果是。 A. NOB. YES C. NO:YESD. 不确定 9. 有语句如下: int n; scanf("%d",&n); 要求当n是奇数时将其显示输出。以下语句中符合要求的是。 A. if(n%2==0)printf("%d\n",n); B. if(n%2)printf("%d\n",n); C. if(n%2=1)printf("%d\n",n); D. if(n/2==1)printf("%d\n",n); 10. 有程序段如下: int flag; char ch; scanf("%c",&ch); flag=ch>='0'&&ch<='9'; if(flag) printf("%c\n",ch); 以下关于程序段执行结果的叙述中正确的是。 A. 当输入一个字符时立即将该字符输出 B. 对输入的任何数值立即输出 C. 若输入一个数字字符,则立即将其输出 D. 若输入的字符是非数字字符,则将其输出 二、 编程题 1. 按照图313所示的流程图编写程序,并指出程序的功能。 2. 按照图314所示的流程图编写程序,并指出程序的功能。 图313编程题1的算法流程图 图314编程题2的算法流程图 3. 计算邮费,邮件的重量由键盘输入。邮件的计费标准为不超过100克时每件10元,超过100克时超出部分每克计费0.5元。 4. 按照货物重量计算运费并输出结果。物流公司按照货物重量分段计费,标准如下: 货物重量不超过50吨时运费为80元/吨,货物重量超过50吨但不超过100吨时超出部分运费为75元/吨,货物重量超过100吨时超出部分运费为70元/吨。 5. 求分段函数y的值,其中x的值由键盘输入。 y=x(x≤0) 2x(0