8、流程控制之switch语句:switch-case结构,break的重要性,default的用法
说到流程控制,if-else 大家肯定都用过。但有一种场景,用 if-else 写出来会特别啰嗦——那就是「多选一」的判断。比如根据用户输入的数字,执行不同的操作。这时候,switch 就派上用场了。
我个人习惯,当判断条件超过三个分支时,优先考虑 switch。不是因为 if-else 不能用,而是 switch 的结构更清晰,一眼就能看出有哪些分支。你想想看,如果看到一串 if-else if-else if... 是不是得数半天?
8.1 switch-case 的基本结构
先看一个最简单的例子:
int score = 85;
int level = score / 10; // 取十位数
switch (level) {
case 9:
printf("优秀\n");
break;
case 8:
printf("良好\n");
break;
case 7:
printf("中等\n");
break;
case 6:
printf("及格\n");
break;
default:
printf("不及格\n");
break;
}
这里 switch 后面跟的是一个整型表达式。每个 case 后面跟一个常量值,然后是一个冒号。程序会从上往下匹配,找到匹配的 case 后,从那里开始执行。
核心要点:
switch表达式必须是整型(char、short、int、long 或枚举类型)case后面必须是常量表达式,不能是变量- 每个
case的值必须唯一,不能重复
8.2 break 的重要性——没有它会发生什么?
嗯,这里要注意。很多初学者会犯一个错误:忘记写 break。为什么会这样?因为觉得「匹配到了就执行完,然后退出」是理所当然的。但 C 语言不是这么设计的。
实际上,switch 是一种「跳转表」机制。匹配到某个 case 后,程序会从那里开始执行,然后一直往下执行,直到遇到 break 或者 switch 结束。这就是所谓的「穿透」(fall-through)。
看这个例子:
int cmd = 2;
switch (cmd) {
case 1:
printf("执行命令1\n");
// 没有 break!
case 2:
printf("执行命令2\n");
// 没有 break!
case 3:
printf("执行命令3\n");
break;
default:
printf("未知命令\n");
break;
}
猜猜输出是什么?
执行命令2
执行命令3
没错,匹配到 case 2 后,它执行了 case 2 的代码,然后继续执行了 case 3 的代码,直到遇到 break 才停下来。
我曾经在项目中遇到过这样的 bug:一个嵌入式设备的按键处理程序,因为少写了一个 break,导致按「音量+」键时,不仅音量增加了,还顺便切换了输入源。排查了整整一个下午才发现是 break 漏了。从那以后,我写 switch 的第一件事就是先把所有 break 写上,再填业务逻辑。
不过,穿透也不全是坏事。有些场景下,我们故意利用穿透来简化代码。比如:
char grade = 'B';
switch (grade) {
case 'A':
case 'B':
case 'C':
printf("及格\n");
break;
case 'D':
case 'E':
printf("不及格\n");
break;
default:
printf("无效成绩\n");
break;
}
这里 A、B、C 都走同一个分支,D、E 走另一个分支。代码简洁多了。但要注意:故意使用穿透时,一定要加注释说明,不然别人(包括三个月后的你自己)会以为你忘了写 break。
8.3 default 的用法
default 相当于 if-else 中的最后一个 else。当所有 case 都不匹配时,就会执行 default 分支。
我个人建议:永远不要省略 default。哪怕你觉得所有情况都已经覆盖了,也写上 default。为什么?
- 代码的可维护性:以后新增一个 case,如果忘记更新 switch,default 能兜底
- 防御性编程:防止意外输入导致程序行为异常
- 代码可读性:告诉阅读者「我已经考虑过所有情况了」
看一个实际例子:
enum State {
IDLE,
RUNNING,
PAUSED,
STOPPED
};
enum State current = IDLE;
switch (current) {
case IDLE:
printf("空闲状态\n");
break;
case RUNNING:
printf("运行中\n");
break;
case PAUSED:
printf("已暂停\n");
break;
case STOPPED:
printf("已停止\n");
break;
default:
// 理论上不会执行到这里,但写上更安全
printf("未知状态,重置到空闲\n");
current = IDLE;
break;
}
小技巧:default 不一定要放在最后。你可以把它放在任何位置,但习惯上放在最后。如果放在中间,记得加 break,否则也会穿透。
8.4 switch 与 if-else 的选择
什么时候用 switch,什么时候用 if-else?我总结了几条经验:
| 场景 | 推荐用 | 原因 |
|---|---|---|
| 判断一个整型变量的多个固定值 | switch | 结构清晰,执行效率高(编译器可能优化为跳转表) |
| 判断范围(如 x > 10 && x < 20) | if-else | switch 不支持范围判断 |
| 判断浮点数或字符串 | if-else | switch 只支持整型 |
| 分支数量少(2-3个) | if-else | 代码更简洁 |
| 分支数量多(4个以上) | switch | 可读性更好 |
8.5 常见陷阱与避坑指南
写 switch 这么多年,我踩过不少坑。分享几个最常见的:
- 忘记 break:这是头号杀手。写完后检查一遍每个 case 后面有没有 break。
- case 值重复:编译器会报错,但如果你用宏定义,不小心定义了两个相同的值,编译时可能不会立即发现。
- 变量声明问题:在 case 里面声明变量需要加花括号,否则可能编译报错。
switch (x) {
case 1:
// 错误:不能在 case 中直接声明变量
// int a = 10;
// 正确做法:加花括号
{
int a = 10;
printf("%d\n", a);
}
break;
default:
break;
}
- switch 表达式类型不匹配:比如用浮点数做 switch 表达式,编译器会报错。
- default 位置不当:如果 default 不在最后且没有 break,会穿透到下一个 case。
8.6 知识体系图
下面这张图总结了 switch 语句的核心知识点:
8.7 总结
switch 语句是 C 语言中非常实用的流程控制工具。说白了,它就是 if-else 的「语法糖」——专门用来处理多分支定值判断的场景。
记住三个关键点:
- break 不能忘——除非你故意要穿透
- default 必须有——哪怕只是打印一条日志
- case 值必须是常量——变量不行,浮点数不行
我在实际项目中,switch 用得最多的场景是状态机解析和命令分发。这两个场景下,switch 比 if-else 清晰得多。你写多了自然会有感觉。
最后一个小建议:写 switch 时,先把所有 case 和 default 的框架搭好,每个后面都写上 break,然后再填充具体逻辑。这样能有效避免漏写 break 的问题。