1. 决策控制
编程语言最初的设计,是为了:
- 反映现实世界的复杂性;
- 灵活性,可以根据不同的情况做出不同的响应。
要搞清楚程序背后的逻辑。
2. if
和if else
基础的语法为:
1 | if (expression) { |
expression
是一个结果为真(true)或假(false)的表达式,当它为真的时候,执行括号里的statement
,当它是假的时候直接跳出括号,继续执行下面的程序。
语法也可以为:
1 | if (expression) { |
这时候,expression
也是一个结果为真(true)或假(false)的表达式,当它为真的时候,执行if
括号里的statement1
,当它是假的时候,执行else
括号里的statement2
。
例如:
1 | uint32_t number = 100; |
这个例子中,if
条件中的表达式判定为真,因此执行其括号中的语句,else
中过的语句直接跳过,不执行。
当然,当条件中的statement
只有一行语句时,大括号可以省略,此时else
与离它最近的一个if
相匹配,但是在实际使用过程中,不管语句有几条,都应该加上大括号,提高代码的可读性。
3. else if
可以在if
和else
语句中间加一个或多个else if
语句,每一个语句对应一个表达式(expression
),此时程序会从上到下一层一层判断每一个expression
是否为真,如果某一个表达式的结果为真,则程序执行其大括号中的所有语句,然后跳出这个if&else
逻辑,如果所有的表达式都为假,则程序执行else
语句中的所有语句。
例如:
1 | uint32_t number = 10; |
程序从上到下,一层一层判断,发现第三个条件为真,则执行其中的语句。
如果所有条件都不满足,则执行else
中的语句:
1 | uint32_t number = 6; |
4. if
嵌套
if
语句可以嵌套使用,也就是在满足某一种条件下再进行判断,是否满足其他条件,并执行对应的语句,例如:
1 | uint32_t number = 5; |
判断第二个条件满足后,程序进行下一组if
语句的判断。
5. 为何要避免嵌套&入门提醒
企业中if
的嵌套不常用,过多的嵌套层次会使得代码难以阅读和维护,如果可以的话,尽量使用一层if-else
来解决问题。
同样的,一个if
中的条件表达式太过复杂,例如包含多个与、或等逻辑,也会使理解和维护代码变得困难。
企业中,如果需要避免用到多层嵌套的情况,会使用策略模式(Strategy Pattern)代替,这属于面向对象的知识,不属于C语言的范畴。
对于面向过程的C语言,一般不适合做这种工作,不过使用结构体,定义策略接口也可以实现类似的功能。
6. 逻辑与或的短路行为
使用逻辑与、逻辑或作为if
中的条件来处理出现较少的情况的行为称之为短路。
例如:事件A和事件B同时满足时,结果输出为true,否则为false:
1 | bool is_event_A = true; |
又例如:事件A和事件B满足其中一个,即输出true,否则为false:
1 | bool is_event_A = true; |
多写!多练习!
7. 访问权限的简单应用案例
下面是一个访问权限的简单案例,展示几种不同的写法。
程序要求当事件A、事件B与事件C满足其中一种情况时,则允许访问,否则拒绝访问。
第一种写法使用if
和else if
对每种条件进行判断:
1 | bool is_event_A = false; |
这种写法的可读性和可扩展性较差,如果还有其他条件时,就需要加入if-else
语句的嵌套,进一步降低可读性。
puts()
是直接输出,对比格式化输出(printf()
)更简洁。
第二种写法是在if-else
条件判断之前先检查访问条件,这种情况下的注释一定要写完整,提高代码的可读性。
1 | bool is_event_A = false; |
这种写法在某些情况下的可读性要好一点,在if-else
之前就已经判断了此时的访问条件。
同样的,也可以将所有判断都放在if
的条件中:
1 | // 如果 is_event_A, is_event_B 或 is_event_C 中的任意一个为 true,则允许访问 |
这种写法也需要写完备的注释,提高程序的可读性。
8. switch...case
的用途
switch...case
语句帮助控制复杂条件和分支运算,switch
语句将控制权转交给其他主题中的语句。
假设事件A、事件B、事件C分别对应stage
参数的1、2、3,每个阶段必须满足对应的事件,才允许访问,否则禁止访问。
1 | uint8_t stage = 2; |
这种写法类似一个电梯,先从switch
中拿到想要去的楼层,对应某一个case
语句,再执行这个对应的case
语句中的代码。在某些情况下,代码可读性和可扩展性都有所提高。
注意每一个case
的最后一定要有break;
语句,使得程序在当前的case
执行完成之后就跳出switch...case
语句,防止继续判断下一个case
的情况发生。
default
与else
类似,当所有条件不符合的时候,则执行当前语句。
企业代码的case
中,一般不会直接写if
条件判断,都是引出外部函数来简化代码:
1 |
|
如果以后需要修改检查事件函数的逻辑,直接找到对应的函数进行修改即可。
9. 再探条件运算符? :
条件运算符也可以嵌套使用,做多层条件的判断,代替多个if-else
语句。
例如,实现一个得分评级程序,90分以上是A,80分到90分是B,70分到80分是C,其他情况是D。这种情况下使用if-else
语句就会比较啰嗦:
1 | uint8_t score = 86; |
使用条件运算符代替:
1 | uint8_t score = 86; |
这样写就比单纯的if-else
语句要简单很多。
写这种条件运算符嵌套的时候尽量分行,提高代码的可读性,当第一个条件不符合的时候,程序跳到第二行,当第二行的条件不符合的时候,跳到第三行。
事实上,上面的条件运算符嵌套与下面的
if-else
语句逻辑相同:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 if (score >= 90) {
grade = 'A';
}
else {
if (score >= 80)
{
grade = 'B';
}
else {
if (score >= 70) {
grade = 'C';
}
else {
grade = 'D';
}
}
}但显而易见,使用条件运算符嵌套,比使用
if-else
嵌套要易读很多。
10. 检查账户锁定案例与提前return
出
案例:输入密码,当输入次数大于3的时候,即锁定账户并结束程序,后面的其他代码不被执行。
1 | int main(void) { |
11. 卫语句的使用:租车案例
案例:当年龄大于或等于22岁,驾驶经验在两年以上,才可以租车,否则不给租赁。
用常规的if-else
语句可以实现:
1 | uint8_t age = 25; |
这种写法判断的是正向的条件,满足所有两个条件,才可以租车。
更好的做法是从反向来判断,运用上一节提到的提前return
出,在不满足条件时直接退出程序,不继续执行:
1 | int main(void) { |
这种写法成为卫语句,在某些情况下可以减少判断次数,且对于设计函数来说,这种语句比较简单。
1 |
|
12. 状态机:switch红绿灯的简单应用
状态机可以使用switch...case
来管理复杂状态转换,例如一个红绿灯的简单程序,默认状态是红灯(0),如果是红灯,则切换到绿灯(1),如果是绿灯,则切换为黄灯(2),如果是黄灯,则切换回红灯。
1 | uint8_t traffic_light_state = 0; // 初始状态为红灯 |
正常来说应该加上循环,使得状态自动进行切换:
1 | uint8_t traffic_light_state = 0; // 初始状态为红灯 |
且状态切换应该放在函数中进行,各种状态也一般会用枚举来定义。
13. switch-case
与if-else
的根本区别
if-else
语句中传入的是一个结果为真(true)或假(false)的表达式,也就是表达式的结果必须是bool
类型;而switch...case
语句最典型的例子就是“电梯”,向switch
中传入的表达式的值必须是常规的整型,不能是其他的任何类型,因此对应的,每个case
的值也需要是整型。
14. 第四章结束语以及警示
卫语句的使用和编程思维很重要!
自己找一些练习题做练习!
多思考多练习案例!
编程是循序渐进的过程:
- 理解代码的逻辑;
- 自己能理解代码为什么要这样写,可以思考明白代码背后的逻辑;
- 足够的训练,动手把代码敲出来。
本文链接: https://hanqingjiang.com/2025/06/29/20250629_C_branchesAndJumps/
版权声明: 本作品采用 CC BY-NC-SA 4.0 进行许可。转载请注明出处!
