4. 选择与复合语句
语句描述程序要执行的动作以及控制流怎样继续。本章先学习一次执行一条表达式语句、把多条语句组成一个块,以及根据条件选择路径;重复执行和跳转留到下一章。
1. 本章内容
语句之间的执行顺序,不等于一个表达式内部各部分的求值顺序;后者仍由表达式规则决定。
2. 习题
#10402
⚡2⏳2
下面的程序把 4.1 与 4.3.1 中的三个片段放进一个文件。逐个写出每个片段的输出,并说明原因。
c
#include <stdio.h>
static void snippet_a(void) {
int ready = 0;
if (ready);
{
printf("A\n");
}
}
static void snippet_b(void) {
int a = 1;
int b = 0;
if (a)
if (b)
printf("inner\n");
else
printf("else\n");
}
static void snippet_c(void) {
int x = 1;
{
int x = 2;
printf("%d ", x);
}
printf("%d\n", x);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
要求:
- 分别给三个片段加上
main调用后编译,用gcc -std=c17 -Wall -Wextra观察警告;把警告抄下来,指出每条警告对应哪个片段。 - 用大括号和复合语句改写
snippet_a与snippet_b:snippet_a改成「条件成立才执行那块代码」,snippet_b改写后else的归属一目了然,不再依赖缩进去猜。 snippet_c里内层x与外层x是什么关系?在内层块之后再写printf("%d\n", x);,读到的是哪个对象?
本机输出:A、else、2 1(三行按调用顺序)。
#10403
⚡3⏳3
写一个整数计算器,从标准输入读入形如 12 + 7 的式子,输出结果。
要求:
- 用
scanf("%d %c %d", &a, &op, &b)读入,检查返回值是否为 3,不是就输出bad input并返回非零值; - 用
switch分派+、-、*、/、%五种运算; - 除数和模数为 0 时输出
division by zero,不得让程序崩溃; - 其余运算符输出
unknown operator; - 每个
case末尾都要有break,并说明少写一个会怎样。
验收:下面六组输入恰好产生对应的六行输出:
text
12 + 7 -> 19
7 / 2 -> 3
7 % 2 -> 1
1 / 0 -> division by zero
1 ^ 2 -> unknown operator
abc + 1 -> bad input1
2
3
4
5
6
2
3
4
5
6
#10404
⚡4⏳3
用 switch 的 fallthrough 计算累进税率。税率表如下,收入以元为单位:
| 级距 | 月收入 | 税率 |
|---|---|---|
| 1 | 不超过 3000 | 3% |
| 2 | 3000 以上至 12000 | 10% |
| 3 | 12000 以上至 25000 | 20% |
| 4 | 25000 以上至 35000 | 25% |
| 5 | 超过 35000 | 30% |
要求:
- 先用
if/else if求出收入落在哪一级,再写一个switch,从最高级往低级依次case,靠故意不写break的 fallthrough 把各级税额累加; - 每一处有意的 fallthrough 都要让
-Wall -Wextra不报-Wimplicit-fallthrough。先用注释/* fall through */试,再换成 C23 的[[fallthrough]];:用-std=c17编译时加不加-pedantic有什么不同?用-std=c2x编译时又怎样?据此说明在 C17 项目里该选哪种写法; - 收入为负时返回 -1;
- 每一级只对落进本级区间的那一段收入计税,最高一级没有上界。照搬「上界减本级下限」的写法时,最高一级的上界该取多大?用
INT_MAX封顶会漏算超过它的收入,形参为什么用long long更合适; - 用这张表验收(本机结果):
0→0、3000→90、5000→290、12000→990、20000→2590、25000→3590、30000→4840、35000→6090、50000→10590、-100→-1。验算:5000 元是 3000×3% + 2000×10%。
#10405
⚡5⏳4
下面的程序用嵌套 if 实现快递运费,逻辑正确但缩进有六层。先读懂它,再用卫语句和 switch 重写,最后验证两者对所有输入的输出完全相同。
c
int shipping_fee(char zone, int grams, bool express) {
int fee;
if (grams > 0) {
if (zone == 'A') {
fee = 800;
if (grams > 1000) {
fee += ((grams - 1000 + 499) / 500) * 300;
}
} else {
if (zone == 'B') {
fee = 600;
if (grams > 1000) {
fee += ((grams - 1000 + 499) / 500) * 400;
}
} else {
if (zone == 'C') {
fee = ((grams + 999) / 1000) * 900;
} else {
return -1;
}
}
}
if (express) {
fee += 500;
}
return fee;
} else {
return -1;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
要求:
- 重写后的函数只在开头用卫语句处理
grams <= 0和未知zone,主路径最多两层缩进; - 用
switch (zone)代替嵌套的zone判断; - 写一个测试函数,对
zone取'A'、'B'、'C'、'D',grams取 0、1、500、1000、1001、1500、1501、2000,express取false、true的全部组合,逐个与上面的原函数比较; - 两者不一致时打印参数和两个结果,全部一致时打印
all N cases match,N 是组合数。
验收:本机原函数对 ('A',500,0)、('A',1001,0)、('A',1501,0)、('B',2000,0)、('C',1001,0)、('A',1000,1)、('D',1000,0)、('A',0,0) 的结果依次为 800、1100、1400、1400、1800、1300、-1、-1。