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 这么多年,我踩过不少坑。分享几个最常见的:

  1. 忘记 break:这是头号杀手。写完后检查一遍每个 case 后面有没有 break。
  2. case 值重复:编译器会报错,但如果你用宏定义,不小心定义了两个相同的值,编译时可能不会立即发现。
  3. 变量声明问题:在 case 里面声明变量需要加花括号,否则可能编译报错。
switch (x) {
    case 1:
        // 错误:不能在 case 中直接声明变量
        // int a = 10;
        // 正确做法:加花括号
        {
            int a = 10;
            printf("%d\n", a);
        }
        break;
    default:
        break;
}
  1. switch 表达式类型不匹配:比如用浮点数做 switch 表达式,编译器会报错。
  2. default 位置不当:如果 default 不在最后且没有 break,会穿透到下一个 case。

8.6 知识体系图

下面这张图总结了 switch 语句的核心知识点:

switch 语句知识体系 switch 语句 基本结构:switch(表达式) { case 常量: ... } break:控制流程跳出,防止穿透 default:兜底分支,处理未匹配情况 穿透特性:可故意利用,但需加注释说明 语法 控制 安全 技巧

8.7 总结

switch 语句是 C 语言中非常实用的流程控制工具。说白了,它就是 if-else 的「语法糖」——专门用来处理多分支定值判断的场景。

记住三个关键点:

  • break 不能忘——除非你故意要穿透
  • default 必须有——哪怕只是打印一条日志
  • case 值必须是常量——变量不行,浮点数不行

我在实际项目中,switch 用得最多的场景是状态机解析和命令分发。这两个场景下,switch 比 if-else 清晰得多。你写多了自然会有感觉。

最后一个小建议:写 switch 时,先把所有 case 和 default 的框架搭好,每个后面都写上 break,然后再填充具体逻辑。这样能有效避免漏写 break 的问题。

专注资料整理