4. 流程控制(上):if语句、if-else语句、else if链、switch-case语句、条件运算符
流程控制,说白了就是让程序学会「看情况办事」。没有流程控制的代码,就像一条笔直的高速公路——从入口一路开到出口,中间没有任何岔路。但现实世界哪有这么简单?你想想看,一个温度传感器读到 85 度,和读到 25 度,处理逻辑能一样吗?
我在嵌入式项目里摸爬滚打这么多年,可以负责任地告诉你:90% 的 bug 都出在流程控制上。不是条件写反了,就是边界没处理好。所以这一章,咱们把流程控制的基础打扎实。
本章核心:掌握 C 语言中五种条件判断机制,理解各自适用场景,学会写出健壮的分支代码。
4.1 if 语句:最简单的判断
if 语句是流程控制的入门砖。它的逻辑很直白:如果条件成立,就执行里面的代码。
if (temperature > 50) {
fan_on(); // 温度超过50度,开风扇
}
这里有个细节我特别想强调:C 语言里「条件成立」指的是表达式结果为非零值。0 代表假,任何非 0 都代表真。所以下面这种写法是合法的:
if (count) { // 等价于 if (count != 0)
process_data();
}
我在项目中见过有人写 if (flag == 1),其实直接写 if (flag) 就够了。少敲几个字符,代码也更干净。
个人习惯:我写 if 语句时,哪怕只有一行代码,也一定会加花括号。为什么?因为后期维护时,别人(甚至你自己)很容易忘记加括号,导致逻辑错误。这个习惯帮我避免过好几次线上事故。
4.2 if-else 语句:二选一
现实中的判断往往不是「做或不做」,而是「做 A 还是做 B」。这时候就需要 if-else 了。
if (battery_level > 20) {
normal_operation();
} else {
low_power_mode();
}
你看,电池电量高于 20% 就正常运行,否则进入低功耗模式。这就是典型的二路分支。
我曾经在一个电机控制项目里,因为 if-else 的条件写反了,导致电机在高温时反而全速运转。那次教训让我养成了一个习惯:写完 if-else 后,一定要用边界值测试一遍。比如这里,我会专门测 battery_level 刚好等于 20 的情况。
注意:if-else 的两个分支是互斥的。如果条件成立,执行 if 块;否则执行 else 块。不会两个都执行,也不会都不执行(除非你忘了写 else,而条件又不成立)。
4.3 else if 链:多路阶梯判断
当你有多个互斥的条件需要依次判断时,else if 链就派上用场了。它像一条流水线——每个条件依次检查,碰到第一个成立的,就执行对应的代码,然后跳出整条链。
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else if (score >= 70) {
grade = 'C';
} else if (score >= 60) {
grade = 'D';
} else {
grade = 'F';
}
这里有个关键点:条件的顺序很重要。如果你把 score >= 80 放在 score >= 90 前面,那考了 95 分的人也会被判定为 B 级——因为第一个条件就成立了。
嗯,说到这个我想起一个真实案例。有个同事写温度报警逻辑,把 temperature > 100 和 temperature > 80 的顺序搞反了。结果温度到了 120 度,系统只报了「高温预警」而不是「紧急停机」。还好是在测试阶段发现的。
经验之谈:写 else if 链时,把最严格的条件放在最前面。比如判断范围时,从大到小排列;判断优先级时,从高到低排列。这样逻辑最清晰,也最容易排查问题。
4.4 switch-case 语句:等值多路选择
当你需要根据一个变量的具体值来做多路选择时,switch-case 比 else if 链更清晰、更高效。
switch (command) {
case CMD_START:
start_motor();
break;
case CMD_STOP:
stop_motor();
break;
case CMD_RESET:
reset_system();
break;
default:
unknown_command();
break;
}
switch-case 有几个容易踩的坑,我一个个说:
- 别忘了 break:每个 case 结尾都要加 break,否则会「穿透」到下一个 case。有时候穿透是有意为之,但大多数时候是 bug。
- case 后面必须是整型常量:不能是变量,也不能是浮点数。像
case 3.14:这种写法,编译器直接报错。 - default 不是必须的:但我建议你每次都写上。哪怕什么都不做,也写个
default: break;,表明你考虑过这个情况。
我个人的选择标准:如果判断条件是「等于某个值」,而且可能的值不超过 5 个,我用 switch-case。如果判断条件是「范围」或者「大于/小于」,我用 if-else。这个习惯让我代码的可读性好了不少。
4.5 条件运算符 ? ::三目运算符的妙用
条件运算符是 C 语言里唯一一个三目运算符。它的格式是:条件 ? 表达式1 : 表达式2。如果条件成立,整个表达式的值等于表达式1,否则等于表达式2。
int max = (a > b) ? a : b; // 取 a 和 b 中的较大值
说白了,这就是一个简化的 if-else。但它有个特点:它是个表达式,不是语句。所以你可以把它嵌在赋值语句、函数参数、甚至另一个条件运算符里面。
不过我要提醒你:别滥用。嵌套的条件运算符可读性极差。比如下面这种:
int result = (x > 0) ? (y > 0 ? 1 : -1) : (y > 0 ? -1 : 1);
你想想看,这代码三天后你自己还能一眼看懂吗?我建议:条件运算符只用于简单的二选一赋值,复杂逻辑还是老老实实用 if-else。
避坑指南:我曾经在一个通信协议解析模块里,用条件运算符嵌套了四层。结果代码 review 时被同事骂了一顿。后来改成 if-else,不仅可读性好了,还发现了一个隐藏的逻辑错误。所以,简洁不等于可读性,别为了少写几行代码牺牲代码的清晰度。
4.6 实战对比:什么时候用哪个?
说了这么多,我们来个实战对比。假设你要写一个函数,根据温度值控制风扇转速:
| 温度范围 | 动作 | 推荐写法 |
|---|---|---|
| t < 25 | 关闭风扇 | if-else 链(范围判断) |
| 25 ≤ t < 50 | 低速 | if-else 链(范围判断) |
| 50 ≤ t < 75 | 中速 | if-else 链(范围判断) |
| t ≥ 75 | 高速 | if-else 链(范围判断) |
如果是根据按键值选择功能:
| 按键值 | 功能 | 推荐写法 |
|---|---|---|
| KEY_UP | 增加音量 | switch-case(等值匹配) |
| KEY_DOWN | 减小音量 | switch-case(等值匹配) |
| KEY_MUTE | 静音 | switch-case(等值匹配) |
你看,选哪种写法,取决于你的判断条件是什么类型。没有绝对的好坏,只有合不合适。
总结一下我的经验:
- 简单二选一 → if-else
- 多路范围判断 → else if 链
- 多路等值判断 → switch-case
- 简单赋值二选一 → 条件运算符
- 复杂逻辑 → 别偷懒,用 if-else
流程控制是程序的骨架。骨架歪了,肌肉再发达也没用。下一节我们会继续深入循环控制,到时候你会发现,很多循环里的坑,其实都是条件判断没写对导致的。先把今天的内容消化掉,后面就顺了。
公众号:蓝海资料掘金营,微信deep3321