这一章在干嘛?
穷尽 C 的全部流程控制语句:if、switch、while、for、do、goto,外加 break/continue 两个「循环内跳转」工具。语法都不难,重点在各自的适用场景和易踩的坑。
4.1 表达式语句与代码块
表达式后面加个分号就成了语句:a = x + 1;、func();。没有副作用的表达式语句(比如孤零零的 x + 1;)毫无意义,编译器通常直接优化掉。用 {} 把多条语句包成一个代码块,在语法上等价于一条语句——所以 if、for 的主体想写多行就加花括号。
悬空 else 与裸分号:
else 总是和 最近的未配对 if 结合。多层嵌套时补全花括号最保险;循环体末尾不小心多个
;(如for(...);)会让循环体变成空语句——循环白跑。
4.2 分支:if 与 switch
if (x > 0)
printf("positive\n");
else if (x == 0)
printf("zero\n");
else
printf("negative\n");
switch (ch) {
case 'a':
case 'e': /* case 落空即共用:元音一起处理 */
printf("vowel\n");
break;
case '0': case '1': /* 数字字符 */
printf("digit char\n");
break;
default:
printf("other\n");
break;
}
switch 的三条硬规则:① 条件必须是整型表达式(浮点、字符串不行);② case 是常量且必须唯一;③ 不 break 就会「贯穿」到下一个 case。贯穿偶尔有用(如上面积累多个 case),但绝大多数时候是漏写 break 的事故——所以每个 case 末尾写 break、实在不需要的加注释说明,是好习惯。
switch 的执行过程:
编译器常把它优化成跳转表,条件值直接算出目标地址,比一串 if-else 链快。case 稠密连续时效果最好。
4.3 循环:while / for / do
| 循环 | 先判断? | 适用场景 |
|---|---|---|
| while (cond) body | 先判断 | 只知道「继续条件」,不知道次数:读输入、链表遍历 |
| for (init; cond; step) | 先判断 | 已知次数或有明确步进;init/step 可留空 |
| do body while (cond); | 后判断 | 至少执行一次:菜单循环、重试逻辑 |
int ch;
while ((ch = getchar()) != EOF) /* 赋值的值作为条件:惯用法 */
putchar(ch);
for (int i = 0; i < n; i++) { /* C99 起 init 里可声明变量 */
if (buf[i] == '\0') break; /* 提前终止 */
if (buf[i] == ' ') continue; /* 跳过本次,直接到 step */
process(buf[i]);
}
break 跳出最近的一层循环或 switch;continue 只结束本轮、跳到步进/条件判断。它们都只影响最内层——想从两层循环里出来,要么加标志位,要么干脆封装成函数用 return。
4.4 goto:能用而不用
goto 能跳到同函数内任意标签处。破坏结构化、难审查,日常代码不该用它。唯二还站得住脚的场景:
/* 1) 深层循环一次跳出 */
for (i = 0; i < N; i++)
for (j = 0; j < M; j++)
if (found(i, j))
goto done;
done:
report_result();
/* 2) 错误集中清理:失败统一跳 cleanup(free(NULL) 无害,正好复用) */
p1 = malloc(A); if (p1 == NULL) goto cleanup;
p2 = malloc(B); if (p2 == NULL) goto cleanup;
/* ... 使用 p1/p2 ... */
cleanup:
free(p2);
free(p1);
本章通关标准:
知道三种循环各适合什么场景;写 switch 想得起 break;看到嵌套 if 配 else 能立刻判断归属。
1. switch 的 default 必须放最后吗?漏 break 会怎样?
default 位置随意(建议最后可读性好),它可以省 break。但普通 case 省 break 会贯穿到下一个 case 继续执行——这是 switch 最常见的 bug 来源。需要故意的贯穿时要加注释标明。
2. while 和 do-while 的本质区别?
while 先判断后执行,条件一开始就假则一次都不执行;do-while 先执行后判断,至少执行一次。适合「先做再问」的场景如菜单、重试。
3. break 和 continue 只影响哪一层?双层循环想直接全退出怎么办?
都只作用于最近的一层循环/switch。双层全退可用标志位让外层也退出,或把整个双循环封装成函数用 return 一跳而出(goto done 也是常用手段)。