29、switch 语句的穿透:break 忘了写会怎样?

说实话,switch 语句的穿透问题,是我在面试新人时最喜欢问的一个点。

为什么?因为十个人里有八个都在这上面栽过跟头。我自己刚入行那会儿,也因为这个 bug 熬过一个通宵。

一、什么是“穿透”?

先看一段代码。你猜猜输出是什么?

int score = 85;
switch (score / 10) {
    case 10:
    case 9:
        printf("优秀\n");
    case 8:
        printf("良好\n");
    case 7:
        printf("中等\n");
    case 6:
        printf("及格\n");
    default:
        printf("不及格\n");
}

很多人以为只输出“良好”。

实际输出是:

良好
中等
及格
不及格

看到了吗?从 case 8 开始,一路往下执行到底。这就是 switch 的“穿透”特性。

核心概念:switch 语句中的 case 只是入口标签,不是隔离墙。一旦匹配成功,就会从那个入口一直执行到 switch 结束,除非遇到 break。

二、穿透是怎么发生的?

switch 语句的底层实现,说白了就是一张跳转表。CPU 根据表达式的值,直接跳到对应的 case 地址开始执行。

它不会在每个 case 后面自动停下来。你想想看,如果每个 case 后面都隐含一个 break,那反而会限制很多灵活用法。

我画了一张图,帮你理解这个流程:

switch(score/10) case 10 / case 9 case 8 ← 匹配 case 7 执行 printf("良好\n") 没有 break,继续穿透 执行 printf("中等\n") default: 执行 printf("不及格\n")

三、穿透的两种场景

1. 忘记写 break(bug)

这是最常见的情况。我见过最离谱的一次,是同事在 case 分支里调了一个函数,函数返回后直接掉进了下一个 case 的逻辑里。那个 bug 在线上跑了两个月才被发现。

警告:忘记写 break 不会报错,编译器也不会警告。这是 C 语言里最安静的杀手之一。

2. 故意利用穿透(feature)

穿透不全是坏事。有些场景下,我们故意利用它来简化代码。

// 多个 case 共享同一段逻辑
switch (cmd) {
    case 'A':
    case 'B':
    case 'C':
        printf("处理 A/B/C 类命令\n");
        break;
    case 'D':
        printf("处理 D 类命令\n");
        break;
    default:
        printf("未知命令\n");
        break;
}

你看,case 'A'case 'B'case 'C' 三个入口共享同一段处理代码。这样写比用 if-else 链清爽多了。

四、避坑指南

我曾经在一个通信协议解析模块里,因为少写了一个 break,导致设备收到了错误的下行指令。那次事故让我养成了几个习惯:

  1. 每个 case 写完立刻写 break——哪怕只有一行代码,也先把 break 写上,再回来填逻辑。
  2. 故意穿透的地方加注释——比如 // fall through,告诉看代码的人:我是故意的。
  3. default 分支永远不省略——哪怕你确信所有情况都覆盖了,也写个 default 兜底。我见过太多因为枚举值新增而导致的穿透 bug。

小技巧:有些编译器(如 GCC)支持 -Wimplicit-fallthrough 警告选项。打开它,编译器会帮你标记那些没有注释的穿透 case。我个人习惯在项目里强制开启这个选项。

五、break 与 if-else 的对比

特性 switch + break if-else 链
可读性 分支清晰,一目了然 嵌套多了容易乱
性能 跳转表,O(1) 匹配 顺序比较,O(n)
灵活性 只能做等值比较 支持范围、条件组合
穿透风险 忘记 break 会出 bug 天然隔离,无穿透

我个人习惯是:能用 switch 的地方尽量用 switch。代码更干净,性能也更好。但前提是——别忘了 break。

六、总结

switch 的穿透,说白了就是一把双刃剑。

用好了,代码简洁优雅。用不好,就是埋雷。

记住一句话:每个 case 要么有 break,要么有注释。做到这一点,你就能避开 90% 的穿透陷阱。

最后送你一个口诀:

case 入口像大门,break 才是关门人。
故意穿透加注释,default 兜底别忘跟。


公众号:蓝海资料掘金营,微信 deep3321