流程控制在开发中必然会用到,其实高中数学也会讲流程控制,但仅仅是初级阶段。
一、流程控制
按照执行流程的不同,可以将平时编写的代码分为 3 大结构:
- 顺序结构:默认的流程结构,按照代码的书写顺序执行每一句代码
- 选择结构:根据表达式的真假性,决定执行哪一段代码
- 循环结构:在表达式为真的情况下,重复执行某一段代码
1.1. 顺序结构
无论执行多少次main
函数,打印结果都是顺序执行:1234
1 | int main() { |
1.2. 选择结构
if
语句if-else
语句else if
语句switch
语句
1.3. 循环结构
while
语句do-while
语句for
语句
二、选择结构
2.1. if 语句
格式:
1 | 代码1; |
特点:
- 如果表达式为假,不会执行大括号中的代码 2;
- 如果表达式为真,就会执行大括号中的代码 2。
示例:
1 | printf("请输入本次考试成绩:"); |
2.2. if-else 语句
格式:
1 | 代码1; |
特点:
- 如果表达式为真,就会执行代码 2,不会执行代码 3;
- 如果表达式为假,就会执行代码 3,不会执行代码 1。
示例:
1 | printf("请输入本次考试成绩:"); |
2.3. if 语句 和 if-else 语句
案例一:
1 | int score = 80; |
案例二:
1 | int score = 80; |
- 案例一的代码更简洁,省掉了不必要的判断(案例二的
score < 80
); - 案例一的代码执行效率更高,只需要进行 1 次表达式判断;
- 案例二的代码需要进行 2 次表达式判断;
score >= 80
score < 80
- 所以推荐使用案例一。
2.4. else if 语句
格式:
1 | if (表达式1) { |
- 表达式对应的大括号代码是一一对应的,如果表达式成立,就执行大括号中的代码(不会再判断后面的表达式),否则进入下一个表达式判断;
- 应该把小范围写在前面,大范围写在后面,否则就永远不会进入后面的表达式判断。
案例:
1 | int score = 80; |
2.5. if 语句 和 else if 语句
案例一:
1 | int score = 80; |
案例二:
1 | int score = 80; |
案例三:
1 | int num = 0; |
三个案例比较后发现,如果判断体是互斥且单一的,用else if
,如果需要多个判断体验证多套逻辑,就用if
。
2.6. 表达式的等价性
案例一:
1 | int score = 90; |
案例二:
1 | int score = 90; |
案例三:
1 | int score = 90; |
以上三个案例都是等价的
2.7. if 语句-省略大括号
if
、else
后可以省略大括号,只会跟其后的第一条语句进行关联。
案例一:
1 | int score = 90; |
案例一等价写法:
1 | int score = 90; |
案例二:
1 | int score = 80; |
案例二等价写法:
1 | int score = 80; |
多个if-else
一起使用的时候,else
后会跟其前面最接近的if
相关联
案例一:
1 | int a = 10; |
案例一等价写法:
1 | int a = 10; |
编译错误案例:
案例一:
1 | int score = 90; |
案例一等价拆解:
1 | int score = 90; |
2.8. 注意点
案例一:
1 | int a = 10; |
案例一分析:if 条件判断后面是一个空实现体,分号代表了 if 结构的结束,所以后面的大括号和 if 没有任何关系
案例二:
1 | int a = 10; |
案例二分析:不管 a 的原始值是多少,都会打印 a 的值,因为条件表达式里面是赋值操作而不是逻辑判断,相当于先给 a 赋值,再判断 a 是否等于 0。所以在开发中一般建议把数值写在左边,变量写在右边,如果这样,即使错把==写成了=,在代码编写及编译阶段就会报错。
2.9. if-else 语句和条件运算符
一些简单的if-else
语句可以使用条件运算符来替代,这样能够使代码更简洁。
1 | int score = 60; |
稍微复杂的代码就不能用条件运算符了:
1 | int score = 60; |
2.10. switch 语句
格式:
1 | switch (表达式) { |
switch 语句用 if 语句表示:
1 | if (表达式 == 表达式1) { |
特点:
- 可以没有
default
语句; - 表达式、表达式 1、表达式 2 都必须是整形表达式;
- 表达式 1、表达式 2 都必须是常量表达式;
case
中的表达式不能重复,并且不能带有变量。
案例:
1 | char score = 'C'; |
case
表达式后面如果没有声明新的变量,大括号可加可不加。如果想在case
表达式后面声明新的变量,那就必须加上大括号,否则编译出错。
案例:
1 | char score = 'C'; |
在
switch
语句中,break
语句的作用就是终止switch
语句执行;如果
case
、default
后面没有break
语句,会出现“贯穿”现象,会继续往下执行其他case
、default
中的代码,直到遇到break
语句或switch
语句的结尾为止;case
、default
没有可以自由调换顺序。
案例:
1 | printf("请输入1~12的月份:"); |
if
和switch
相互替代
1 | int num = 0; |
思考:什么时候用
if
,什么时候用switch
? 当表示一个范围时,使用if
,当判断一个变量是否等于固定值时,两者都可以,但只要switch
能表达的,必然都可以用if
。
三、循环结构
循环结构包括:
while
语句do-while
语句for
语句
3.1. while 语句
格式:
1 | while (表达式) { |
while
语句也叫while
循环,while
语句大括号中的代码,称为循环体。
特点:
- 如果表达式为假,不会执行循环体,直接终止
while
语句; - 如果表达式为真,执行循环体;
- 执行完循环体后,会再次回到第一步,根据表达式的真假来做出选择;
while
语句执行完成后,才会继续往下执行代码 1,否则将会一直循环,直到循环结束。
案例一:(输入正整数 n,使用while
语句依次打印区间[1, n]的整数)
1 | // 方法一: |
案例二:(输入一个 1 ~ 100 之间的正整数 n,求 1~n 之间的整数相加结果)
1 | int n = 0; |
案例二延伸:(求 1~n 之间奇数相加,偶数相减的结果)
1 | int n = 0; |
案例二延伸:(求 1~n 之间奇数相加的结果)
1 | int n = 0; |
3.2. for 语句
格式:
1 | for (代码1; 表达式; 代码2) { |
for
语句也叫for
循环,for
语句大括号中的代码,称为循环体。
特点:
- 无论表达式是真还是假,都会先执行代码 1;
- 如果表达式为假,不会执行循环体、代码 2,直接终止
for
语句; - 如果表达式为真,依次执行循环体、代码 2;
- 执行完循环体后,会再次回到第二步,根据表达式的真假来做出选择;
for
语句执行完成后,才会继续往下执行代码 3,否则将会一直循环,直到循环结束。
注意点:
- 代码 2 后面不能加分号(;);
- 无论表达式是真是假,都会在第一次执行代码 1,并且在 for 语句后面的执行过程中,都不会在执行代码 1。
3.3. for 语句和 while 语句比较
1 | for (代码1; 表达式; 代码3) { |
案例:
1 | for (int i = 1; i <= 5; i++) { |
根据代码的执行顺序我们可以得出下面的结论:
- 表达式后面的代码可以拿出来
1 | for (int i = 1; i <= 5;) { |
- 表达式后面可以使用逗号运算符
1 | for (int i = 1; i <= 5; printf("%d\n", i), i++) { |
- 表达式前面声明变量时可以同时声明多个变量
1 | for (int i = 1, j = 0; i <= 5; i++, j += 1) { |
- 表达式前后的分号不能省略
1 | int i = 1; |
案例:(输入一个 1 ~ 100 之间的正整数 n,求 1~n 之间的整数相加结果)
1 | int n = 0; |
上面的案例可以看出,while
循环写出的代码一定可以用for
循环实现。只是有时候while
写出的代码可能会更加简洁,在实际开发过程中应根据业务场景选择更合适的循环语句。
3.4. do-while 语句
格式:
1 | do { |
特点:
- 无论表达式是真是假,先执行一次循环体;
- 如果表达式为假,不会执行循环体,直接终止
while
语句; - 如果表达式为真,就会执行循环体;
- 执行完循环体后,会再次回到第二步根据表达式的真假来做出选择。
注意点:
while
表达式后面一定要加分号(;
);- 无论表达式真假,都会在开头先执行一次循环体。
示例:
1 | int i; |
总结:很多时候,使用while
、do-while
,for
可以完成一样的功能,只是书写格式不一样。
案例一:
(场景:提示输入一个 1 ~ 100 之间的正整数 n,如果输入错误,要求重新输入,直到输入正确为止)
1 | int n; |
案例一对比:(使用while
语句比较)
1 | int n = 0; |
总结:对比while
语句发现,do-while
语句会少一次最初没有必要的判断,因为不论表达式真假,都会先执行一次循环体。
3.5. 循环嵌套
场景一:严格要求输入一个 1~20 之间的正整数 n,输入 n 行 n 列的图形,图形用#
表示。
图示:
1 | 请输入一个1~20之间的正整数:5 |
代码:
1 | int n; |
场景二:严格要求输入一个 1~20 之间的正整数 n,输入 n 行 n 列的图形,图形用#
表示。
图示:
1 | 请输入一个1~20之间的正整数:5 |
代码:
1 | int n; |
场景三:严格要求输入一个 1~20 之间的正整数 n,输入 n 行 n 列的图形,图形用#
表示。
图示:
1 | 请输入一个1~20之间的正整数:5 |
代码:
1 | int n; |
场景四:严格要求输入一个 1~20 之间的正整数 n,输入 n 行 n 列的图形,图形用#
表示。
图示:
1 | 请输入一个1~20之间的正整数:5 |
代码:
1 | int n; |
场景五:严格要求输入一个 1~20 之间的正整数 n,输入 n 行 n 列的图形,图形用#
表示。
图示:
1 | 请输入一个1~20之间的正整数:5 |
代码:
1 | int n; |
场景六:输出九九乘法表
图示:
1 | 1x1=1 |
代码:
1 | for (int i = 1; i <= 9; i++) { |
3.6. break 语句
break
语句只用在while
、do-while
、for
、switch
语句中,它的作用就是终止并跳出所在的循环语句。
示例:
1 | for (int i = 1; i <= 5; i++) { |
3.7. continue 语句
continue
语句只用在while
、do-while
、for
语句中,它的作用是跳过所在循环语句的循环体剩余部分
示例:
1 | for (int i = 1; i <= 5; i++) { |
3.8. goto 语句
goto
语句的作用:可以为所欲为地灵活跳转
示例:
1 | int n = 1; |
goto
语句使用起来非常灵活,在一定情况下,可以提高程序的效率,但是他破坏了结构化的设计风格,容易造成代码执行流程的混乱(一般情况下,代码的执行顺序是从上到下),导致代码难以调试和维护。所以非常不建议使用goto
语句(这也是在开发中几乎看不到goto
语句的原因)。