4.7 嵌套if语句和多选项决策
到现在为止,if语句仅用来对具有一个或两个选项的决策编码。在这一小节将使用嵌套if语句(一个if语句包含在另一个if语句中)对具有多个选项的决策编码。
嵌套if语句(nested if statement) if语句中条件为真或假时的任务是另一个if语句。
例4-15 下面的嵌套if语句具有三个选项。它根据x大于零、小于零或者等于零分别将三个变量(num_pos、num_neg或者num_zero)加1。框图给出了嵌套if语句的逻辑结构:第二个if语句是第一个if语句条件为false时执行的任务(else之后的任务)。
/* increment num_pos, num_neg, or num_zero depending on x */
if (x > 0)
num_pos = num_pos + 1 ;
else
if (x < 0)
num neg =num_neg + 1;
else /* x equals 0 */
num_zero = num_zero + i;
该嵌套if语句的执行过程如下:测试第一个条件(x > 0),如果条件为true,num_pos增加并且if语句的其余部分被跳过;如果第一个条件为false,测试第二个条件(x < 0),如果条件为true,num_neg增加,否则num_nero增加。意识到第二个条件只在第一个条件为false时才进行测试非常重要。表4-10在x为-7时跟踪这条语句的执行。由于x > 0为false,第二个条件(x < 0)被测试。
表4-10 例4-15中x=-7时的if语句跟踪
|
语句部分 |
作 用 |
|
if (x > 0) |
-7 > 0 为false |
|
if (x < 0) |
-7 < 0 为true |
|
num_neg = num_neg + 1 |
num_neg加1 |
4.7.1 比较嵌套if和if序列
初级程序员有时更愿意使用if语句序列而不是单个嵌套if语句。例如,例4-15中的嵌套if语句被重写为if语句序列:
if (x > 0)
num pos =num_pos + i;
if (x < 0)
num_neg =num_neg + i;
if (x == 0)
num_zero =num_zero + i;
虽然这个序列和最初的嵌套if语句在逻辑上等价,但它既不易读也没有效率。和嵌套if语句不同,这个序列不会清楚地表明当x取特定值时三条赋值语句中只有一条被执行。由于三个条件都会被测试,因此它的效率也不高。在嵌套if语句中,当x为正值时只有第一个条件被测试。
4.7.2 嵌套if的多选项决策形式
嵌套if语句可以非常复杂。如果选项超过三个并且缩进不一致,确定if语句的逻辑结构非常困难。在例4-15中,每一个条件为false时的任务(除了最后一个)都紧跟一个if-then-else语句,对于这种情况可以将嵌套if编码为以下的多选项决策形式。
多选项决策
语法: if (条件1)
语句1
else if (条件2)
语句2
.
.
.
else if (条件n)
语句n
else
语句e
示例:/* increment num_pos, num_neg, or num_zero depending
on x */
if(x>0)
num_pos = num_pos + 1;
else if (x<0)
num_neg = num_neg + 1;
else /* x equals 0 */
num_zero = num_zero + 1;
说明:多选项决策中的条件按顺序求值,直到某个条件为true。如果某个条件为true,其后的语句被执行,多选项决策的其余部分被跳过。如果某个条件为false,其后的语句被跳过,并测试下一个条件。如果所有的条件都是false,那么else之后的语句e被执行。
注意:在多选项决策中,单词else和下一个条件的if出现在同一行。所有的else都对齐,并且每一个相关的语句在控制该语句执行的条件之下缩进。
例4-16 假设希望将用分贝表示的噪声强度和噪声效果联系起来。下面的表格给出了噪声等级和噪声的人体感觉之间的联系。
|
分贝(db)表示的强度 |
感 觉 |
分贝(db)表示的强度 |
感 觉 |
|
50或低于50 |
安静 |
91~110 |
非常令人烦恼 |
|
51~70 |
烦扰 |
110以上 |
心神不宁 |
|
71~90 |
令人烦恼 |
下面的多选项决策根据这个表格显示噪声的感觉。如果噪声测量为62分贝,后三个条件求值为true,但是由于第一个为true的条件是noise_db <= 70,“62-decibel noise is instrusive.”将被显示。
/* Display perception of noise loudness */
if (noise_db <= 50)
printf("%d-decibel noise is quiet.\n", noise_db);
else if (noise_db <= 70)
printf("%d-decibel noise is intrusive.\n", noise_db);
else if (noise_db <= 90)
printf("%d-decibel noise is annoying.\n", noise_db);
else if (noise_db <= 110)
printf("%d-decibel noise is very annoying.\n", noise_db);
else
printf("%d-decibel noise is uncomfortable.\n", noise_db);
4.7.3 多选项决策中条件的顺序
当多选项决策中多个条件为true时,只有第一个为true的条件之后的任务执行。因此,条件的顺序会影响输出结果。
按以下方式编写条件是不正确的,因为除了最大的声音(110分贝以上)外,第一个条件总为true并且其他条件都将被跳过,其他声音都被错误地分类为“非常令人烦恼”。
/* incorrect perception of noise loudness */
if (noise_db <= 110)
printf("%d-decibel noise is very annoying.\n", noise_db);
else if (noise_db <= 90)
printf("%d-decibel noise is annoying.\n", noise_db);
else if (noise_db <= 70)
printf("%d-decibel noise is intrusive.\n", noise_db);
else if (noise_db <= 50)
printf("%d-decibel noise is quiet.\n", noise_db);
else
printf("%d-decibel noise is uncomfortable.\n", noise_db);
条件的顺序还会影响程序的效率。如果我们知道大的噪声比小的噪声可能性大得多,更有效的方式是首先测试110分贝以上的噪声级别,然后测试91和110之间的级别,依此类推。
例4-17 可以使用多选项if语句来实现描述几个选项的决策表。例如,假设你是一个会计师,根据表4-11建立工资单,该表给出了最高$150 000.00的5种不同工资范围。表格的每一行给出了特定工资范围(第1列)的基本税额(第2列)和超额百分比(第3列)。给定一个人的工资,可以计算出总税额,计算方法是用基本税额加上超额百分比与超出该工资范围最低限额的工资的乘积。
例如,表格的第二行说明工资为20 000.00美元的税额是2 250.00美元加上超过15 000.00美元部分的18%(即5 000.00美元的18%,或900.00美元),因此,总税额是2 250.00美元加900.00美元或者3 150.00美元。
函数comp_tax(图4-10)中的if语句实现了这个税额表。如果salary的值在表格范围内(0.00到150 000.00),恰好有一条给tax赋值的语句会执行。表4-12给出了salary为25 000.00美元时的if语句跟踪。可以看到赋予tax的值4 050.00美元是正确的。
表4-11 例4-17的决策表
|
工资范围($) |
基本税额($) |
超额百分比 |
|
0.00~14 999.99 |
0.00 |
15 |
|
15 000.00~29 999.99 |
2 250.00 |
18 |
|
30 000.00~49 999.99 |
5 400.00 |
22 |
|
50 000.00~79 999.99 |
11 000.00 |
27 |
|
80 000.00~150 000.00 |
21 600.00 |
33 |
|
513. /* 514. * Computes the tax due based on a tax table. 515. * Pre : salary is defined. 516. * Post: Returns the tax due for 0.0 <= salary <= 150,000.00; 517. * returns -1.0 if salary is outside the table range. 518. */ 519. double 520. comp_tax(double salary) 521. { 522. double tax; 523. 524. if (salary < 0.0) 525. tax = -1.0; 526. else if (salary < 15000.00) /* first range */ 527. tax = 0.15 * salary; 528. else if (salary < 30000.00) /* second range */ 529. tax = (salary - 15000.00) * 0.18 + 2250.00; 530. else if (salary < 50000.00) /* third range */ 531. tax = (salary - 30000.00) * 0.22 + 5400.00; 532. else if (salary < 80000.00) /* fourth range */ 533. tax = (salary - 50000.00) * 0.27 + 11000.00; 534. else if (salary <= 150000.00) /* fifth range */ 535. tax = (salary - 80000.00) * 0.33 + 21600.00; 536. else 537. tax = -1.0; 538. 539. return (tax); 540. } |
图4-10 函数comp_tax
表4-12 salary = $25 000.00时图4-10中if语句的跟踪
|
语句部分 |
salary |
tax |
结 果 |
|
25000.00 |
? |
||
|
if(salary < 0.0) |
25 000.0 <0 为false |
||
|
else if(salary < 15 000.00) |
25 000.0 < 15 000.0为false |
||
|
else if(salary < 30 000.00) |
25 000.0 < 30 000.0为true |
(续)
|
语句部分 |
salary |
tax |
结 果 |
|
tax = (salary – 15 000.00) |
求值为10 000.00 |
||
|
* 0.18 |
求值为1 800.00 |
||
|
+ 2 250.00; |
4 050.00 |
求值为4 050.00 |
4.7.4 程序风格:确认变量的值
如果在使用变量进行计算前确定变量的值,可以避免无效或无意义的数据。如果salary的值在表覆盖的范围(0.0~150 000.00)之外,函数comp_tax返回-1.0(一个不可能的税额)而不是计算不正确的税额。第一个将tax设为-1.0的条件是salary是负数。如果salary大于150 000.00,所有条件求值为false,因此else之后的任务也将tax设为-1.0。如果comp_tax返回-1.0,调用comp_tax的函数应显示一条错误信息。
4.7.5 具有多个变量的嵌套if语句
在前面的大多数例子中,嵌套if语句用于测试单个变量的值,因此能够将每个嵌套if语句写作多选项决策。如果决策中涉及几个变量,就不能总是使用一个多选项决策。在例4-18包含的情况下,可以用嵌套if语句作为“过滤器”来选择满足几个不同标准的数据。
例4-18 美国国防部想要开发一个识别18~26岁(包括18岁和26岁)的单身男性的程序。完成这项任务的一种方法是使用嵌套if语句,它的条件只在前面的所有标准都满足时才测试下一个标准。在下面的嵌套if语句中,假设所有变量都有值。只有所有条件都为true时,调用printf的语句才执行。
/* Print a message if all criteria are met. */
if (marital status == 'S')
if (gender == 'M')
if (age >= 18 && age <= 26)
printf("All criteria are met.\n");
下面的等效语句使用了具有复合条件的单个if:
if (marital_status == ' S ' && gender == 'M'
&& age >= 18 && age <= 26)
printf("All criteria are met. \n") ;
例4-19 你正在开发一个用来控制主要隧道出口处的警告标志的程序。如果道路平滑(road_status为'S'),你希望根据路面是否湿滑或结冰提醒司机刹车时间应是原来的两倍或者四倍。你的程序还要获得当前的摄氏温度(temp),因此检查温度在零度以上还是零度以下能够使你选择正确的消息。以下的嵌套if语句概括了需要遵循的决策过程,图4-11所示的流程图表示了这一过程。
if (road_status == 'S')
if (temp > 0) {
printf("Wet roads ahead\n");
printf("Stopping time doubled\n");
} else {
printf("Icy roads ahead\n");
printf("Stopping time quadrupled\n");
}
else
printf("Drive carefully!\n");

图4-11 道路标志决策过程的流程图
为了验证例4-19中的嵌套if语句是否正确,需要对所有可能的道路状况值和温度的组合跟踪执行。流程图最右边的输出只有在所有条件都为true时才执行。当包括road_status的条件为false时,最左边的输出总是执行。中间的输出在包括road_status的条件为true但包括temp的条件为false时执行。
在编写嵌套if语句时,应该知道C语言将else和最近一个不完整的if关联。例如,如果道路标志决策的第一个else省略,剩下的代码如下所示:
/* incorrect interpretation of nested if */
if (road_status == 'S')
if (temp > 0) {
printf("Wet roads ahead\n");
printf("Stopping time doubled\n");
}
else
printf("Drive carefully!\n");
虽然缩进会使你认为else仍然是第一个if的条件为false的分支,但C编译器将它看作第二个if的条件为false的分支。这样的缩进应该和以下语句的实际意义匹配。
/* correct interpretation of nested if */
if (road_status == 'S')
if (temp > 0) {
printf("Wet roads ahead\n");
printf("Stopping time doubled\n");
} else
printf("Drive carefullv!\n");
要强制else是第一个if的条件为false的分支,可以将第一个决策的条件为true时的任务放到大括号中。
/* interpretation with braces around first true task */
if (road_status == 'S') {
if (temp > 0) {
printf("Wet roads ahead\n");
printf("Stopping time doubled\n");
}
} else
printf("Drive carefully!\n");
注意,由于第二个决策(temp > 0)落在第一个决策的条件为true的分支内,因此不能用一个多选项决策语句来实现图4-11所示的流程图。但是,如果改变初始条件使得条件分支改变,多选项结构就可以工作。要实现这个目标只需简单地将条件改为检测道路是否干燥。
if (road_status == 'D') {
printf("Drive carefully!\n");
} else if (temp > 0) {
printf("Wet roads ahead\n");
printf("Stopping time doubled\n");
} else {
printf("Icy roads ahead\n");
printf("Stopping time quadrupled\n");
}
只有道路干燥时第一个条件为true。第二个条件只有在第一个条件失败时才测试,因此它的相关语句只有在道路不干燥并且温度在零度以上时才会执行。最后,else子句只有在前两个条件都失败时才执行,即道路不干燥并且温度不超过零度。
练习
自测
1. 给定工资为$23 500.00,跟踪图4-10中的嵌套if语句的执行。
2. 交换图4-10中if语句的前两个条件的顺序会造成什么结果?
3. 为给出的决策流程图编写嵌套if语句,要求尽可能为中间决策使用多选项if。

编程
1. 重写例4-16的if语句,在所有条件中只使用关系运算符>。
2. 使用嵌套if语句实现以下决策表,假设年平均学分的范围是0.0~4.0。
|
年平均学分 |
成绩单信息 |
|
0.0 ~ 0.99 |
学期不及格——暂停注册 |
|
1.0 ~1.99 |
下学期留校查看 |
|
2.0 ~ 2.99 |
(无消息) |
|
3.0 ~ 3.49 |
本学期的优秀学生榜 |
|
3.5 ~ 4.00 |
本学期的最高荣誉榜 |
3. 使用多选项if语句实现以下决策表,假设风速为整数。
|
风 速 |
类 别 |
风 速 |
类 别 |
|
25以下 |
非强风 |
55~72 |
狂风 |
|
25~38 |
强风 |
72以上 |
飓风 |
|
39~54 |
暴风 |
4. 空军要求你编写一个程序来标明一个超音速飞行器是军用飞机还是民用飞机。将飞机的观测速度(以km/h为单位)和估计长度(以米为单位)作为程序输入。对于速度超过1 100 km/h的飞机,程序应该将长度超过52m的飞机标明为“民用”,短于52m的标明为“军用”。对于速度较低的飞机,程序应该给出的消息是“飞机类型未知”。







