switch 语句
switch 语句是多分支选择结构,根据整数表达式的值跳转到对应的 case 标签处执行。与 if-else if 链相比,switch 在分支较多时更清晰,且某些编译器可以优化为跳转表,执行效率更高。但 switch 也有其局限性:条件必须是整型常量表达式,且 case 穿透(Fall-through)需要特别注意。
基本语法
switch (表达式) {
case 常量表达式1:
语句;
break;
case 常量表达式2:
语句;
break;
default:
语句;
break;
}
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
case 4:
printf("Thursday\n");
break;
case 5:
printf("Friday\n");
break;
case 6:
case 7:
printf("Weekend\n");
break;
default:
printf("Invalid day\n");
break;
}
case 穿透
switch 的一个独特特性是"穿透"——如果没有 break,执行会继续到下一个 case:
char grade = 'B';
switch (grade) {
case 'A':
printf("Excellent\n");
break;
case 'B':
printf("Good\n"); /* 执行 */
/* 没有 break,穿透到 C */
case 'C':
printf("Average\n"); /* 也执行! */
break;
case 'D':
printf("Poor\n");
break;
default:
printf("Invalid\n");
}
/* 输出:
Good
Average
*/
穿透有时是有意利用的(如多个 case 共享代码),但更多时候是遗漏 break 导致的 Bug。现代编译器会对没有 break 的 case 发出警告(-Wimplicit-fallthrough),可以用注释抑制:
switch (cmd) {
case 'q':
case 'Q':
quit = true;
break; /* 两个 case 共享代码 */
case 's':
save_file();
/* fall through */ /* 注释说明有意穿透 */
case 'S':
save_as();
break;
}
case 标签的限制
case 后面必须是整型常量表达式:
const int VALUE = 5;
switch (x) {
case 1: /* 合法:整数常量 */
case 2 + 3: /* 合法:常量表达式 */
case VALUE: /* C99:const int 在某些编译器允许 */
/* 但严格来说不是常量表达式 */
/* case x: */ /* 错误:变量 */
/* case 1.5: */ /* 错误:浮点数 */
}
C99 标准规定 case 标签必须是整数常量表达式,const int 变量不是常量表达式(C++ 中才是)。但某些编译器作为扩展允许 const int。
default 标签
default 标签处理所有未被 case 匹配的情况:
switch (cmd) {
case 'y':
case 'Y':
confirmed = true;
break;
case 'n':
case 'N':
confirmed = false;
break;
default:
printf("Please enter Y or N\n");
break;
}
default 可以放在 switch 的任何位置(不一定在最后),但放在最后最符合阅读习惯。
switch 与 if-else 的选择
| 场景 | 推荐 |
|---|---|
| 离散整数值判断(如菜单选择、状态码) | switch |
范围判断(如 x > 0 && x < 10) | if-else |
| 浮点数判断 | if-else |
| 复杂条件组合 | if-else |
| 字符串判断 | if-else(C 语言 switch 不支持字符串) |
/* switch 适合 */
switch (error_code) {
case ERR_OK: /* ... */ break;
case ERR_IO: /* ... */ break;
case ERR_MEMORY: /* ... */ break;
}
/* if-else 适合 */
if (score >= 90) /* ... */
else if (score >= 80) /* ... */
else if (score >= 70) /* ... */
常见错误
遗漏 break:
switch (cmd) {
case 'a':
do_a();
/* 忘记 break */
case 'b':
do_b(); /* 当 cmd 为 'a' 时,do_a() 和 do_b() 都会执行! */
break;
}
case 后使用变量:
int x = 5;
switch (y) {
case x: /* 错误:x 不是常量表达式 */
/* ... */
}
浮点数 switch:
double d = 1.5;
switch (d) { /* 错误:switch 表达式必须是整数 */
/* ... */
}
空 switch:
switch (x) {
} /* 合法但无意义 */
最佳实践
- 每个
case都写break,除非有意利用穿透 - 有意穿透时加注释说明
/* fall through */ - 始终包含
default处理意外情况 default放在最后- 多个
case共享代码时,将case标签连续写 - 复杂的
switch考虑用函数指针表替代