C语言 09 流程控制
if
如果需要判断某个条件,当满足此条件时,才执行某些代码,那这个时候该怎么办呢?可以使用if
语句来实现:
#include <stdio.h>
int main() {
int i = 0;
// 只希望i大于10的时候才执行下面的打印语句
if (i > 10) {
printf("该数字大于10");
}
// 后面的代码在if之外,无论是否满足if条件,都跟后面的代码无关,所以这里的代码任何情况下都会执行
printf("必定输出");
}
必定输出
if 语句的标准格式如下:
if(判断条件) {
执行的代码
}
当然如果只需要执行一行代码的话,可以省略花括号:
if(判断条件)
// 注意这样只有后一行代码生效,其他的算作if之外的代码了
一行执行的代码
这样虽然简洁,但不够直观,还是建议使用花括号。
现在需求升级了,需要判断某个条件。当满足此条件时,执行某段代码;当不满足时,执行另一段代码。可以结合 else
语句来实现:
#include <stdio.h>
int main() {
int i = 0;
// 满足if条件才执行
if (i > 10) {
printf("该数字大于10");
}
// 不满足if条件才执行
else {
printf("该数字不大于10");
}
}
该数字不大于10
但是这样可能还是不够用,比如现在希望判断学生的成绩,不同分数段打印的等级不一样。比如90以上就是优秀,70以上就是良好,60以上是及格,其他的都是不及格。那么这种又该如何判断呢?要像这样进行连续判断,我们需要使用 else-if
来完成:
#include <stdio.h>
int main() {
int score = 60;
if (score >= 90) {
printf("优秀");
} else if (score >= 70) {
printf("良好");
} else if (score >= 60) {
printf("及格");
} else {
printf("不及格");
}
}
及格
if
这类的语句(包括后面介绍的三种)都是支持嵌套使用的。
比如现在希望低于60分的同学需要补习,0-30分需要补语文,30-60分需要补数学,这时就需要用到嵌套:
#include <stdio.h>
int main() {
int score = 37;
// 先判断不及格
if (score < 60) {
// 在内层再嵌套一个if语句进行进一步的判断
if (score > 30) {
printf("学习数学");
} else {
printf("学习语文");
}
}
}
学习数学
switch
前面介绍了 if 语句,可以通过一个 if 语句轻松地进行条件判断,然后根据对应的条件,来执行不同的逻辑。
当然除了这种方式之外,也可以使用 switch
语句来实现,它更适用于多分支的情况:
// 需要传入一个目标,比如变量,或是计算表达式等
switch (目标) {
// 如果目标的值等于这里给定的匹配值,那么就执行case后面的代码
case 匹配值:
代码...
// 代码执行结束后需要使用break来结束,否则会继续溜到下一个case继续执行代码
break;
}
比如现在要根据学生的等级进行分班,学生有 A、B、C 三个等级:
#include <stdio.h>
int main() {
char c = 'A';
// 这里目标就是变量c
switch (c) {
// 分别指定ABC三个匹配值,并且执行不同的代码
case 'A':
printf("好班");
// 执行完之后一定记得break,否则会继续向下执行下一个case中的代码
break;
case 'B':
printf("平行班");
break;
case 'C':
printf("差班");
break;
}
}
好班
switch
可以精准匹配某个值,但是它不能进行范围判断,比如我们要判断分数段,这时用 switch 就很鸡肋了。
当然除了精准匹配之外,其他的情况我们可以用 default 来表示:
switch (目标) {
case: ...
default:
其他情况下执行的代码
}
比如:
#include <stdio.h>
int main() {
char c = 'D';
switch (c) {
case 'A':
printf("好班");
break;
case 'B':
printf("平行班");
break;
case 'C':
printf("差班");
break;
// 其他情况一律就是下面的代码了
default:
printf("去读高职");
}
}
去读高职
当然 switch 中可以继续嵌套其他的流程控制语句,比如 if:
#include <stdio.h>
int main() {
int score = 95;
char c = 'A';
switch (c) {
case 'A':
// 嵌套一个if语句
if (score > 90) {
printf("去春招班");
} else {
printf("去好班");
}
break;
case 'B':
printf("去平行班");
break;
}
}
去春招班
for
通过前面的学习,了解了如何使用分支语句来根据不同的条件执行不同的代码,接着来看第二种重要的流程控制语句,循环语句。
在某些时候,可能需要批量执行某些代码:
#include <stdio.h>
int main() {
// 把这句话打印三遍
printf("不要回答!");
printf("不要回答!");
printf("不要回答!");
}
遇到这种情况,如果还没学习循环语句,就只能写 N 次来实现。
现在可以使用 for
循环语句来多次执行:
for (表达式1; 表达式2; 表达式3) {
循环体
}
- 表达式1:在循环开始时仅执行一次。
- 表达式2:每次循环开始前会执行一次,要求为判断语句,用于判断是否可以结束循环,若结果为真,那么继续循环,否则结束循环。
- 表达式3:每次循环完成后会执行一次。
- 循环体:每次循环都会执行循环体里面的内容,直到循环结束。
一个标准的 for 循环语句写法如下:
#include <stdio.h>
int main() {
// 比如现在希望循环3次
// 首先定义一个变量i用于控制循环结束
// 表达式2在循环开始之前判断是否小于3
// 表达式3每次循环结束都让i自增一次,这样当自增3次之后不再满足条件,循环就会结束,正好3次循环
for (int i = 0; i < 3; i++) {
printf("不要回答!");
}
}
不要回答!不要回答!不要回答!
注意,如果表达式 2 什么都不写,那么会默认判定为真:
#include <stdio.h>
int main() {
// 表达式2不编写任何内容,默认为真,这样的话循环永远都不会结束
for (int i = 0; ; i++) {
printf("%d, ", i);
}
}
这样的死循环会一直占用系统资源,需要谨慎使用。
所以,如果想要编写一个无限循环,其实表达式 1、2、3 都不用写:
#include <stdio.h>
int main() {
// 表达式 1、2、3 都不写直接无限循环,但是注意,两个分号还是要写的
for (;;) {
printf("不要回答!");
}
}
当然,也可以在循环过程中提前终止或是加速循环的进行,这里需要认识两个新的关键字 break
:
#include <stdio.h>
int main() {
for (int i = 0; i < 10; ++i) {
// 现在希望在满足某个条件下提前终止循环,可以使用break关键字来跳出循环
if (i == 5) {
break;
}
printf("%d", i);
}
}
01234
可以看到,当满足条件时,会直接通过 break 跳出循环,循环不再继续下去,直接结束掉。
也可以加速循环:
#include <stdio.h>
int main() {
for (int i = 0; i < 10; ++i) {
// 使用continue关键字会加速循环,无论后面有没有未执行完的代码,都会直接开启下一轮循环
if (i == 5) {
continue;
}
printf("%d", i);
}
}
012346789
虽然使用 break 和 continue 关键字能够更方便的控制循环,但是注意在多重循环嵌套下,它只对离它最近的循环生效(就近原则):
#include <stdio.h>
int main() {
for (int i = 1; i < 4; ++i) {
for (int j = 1; j < 4; ++j) {
// 当i == j时加速循环
if (i == j) {
continue;
}
printf("%d, %d\n", i, j);
}
}
}
1, 2
1, 3
2, 1
2, 3
3, 1
3, 2
可以看到,continue 仅仅加速的是内层循环,而对外层循环没有任何效果,同样的,break 也只会终结离它最近的:
#include <stdio.h>
int main() {
for (int i = 1; i < 4; ++i) {
for (int j = 1; j < 4; ++j) {
// 当i == j时终止循环
if (i == j) {
break;
}
printf("%d, %d\n", i, j);
}
}
}
2, 1
3, 1
3, 2
while
前面介绍了 for 循环语句,接着来看第二种 while 循环。
for 循环要求填写三个表达式,而 while 相当于是一个简化版本,它只需要填写循环的维持条件即可,比如:
#include <stdio.h>
int main() {
// 每次循环开始之前都会判断括号内的内容是否为真,如果是就继续循环
while (1) {
// 这里会无限循环
printf("Hello World!\n");
}
}
相比 for 循环,while 循环更多的用在不明确具体的结束时机的情况下,而for 循环更多用于明确知道循环的情况。
比如明确要进行循环 10 次,此时用 for 循环会更加合适一些
又比如只知道当 i 大于 10 时需要结束循环,但是 i 在循环多少次之后才不满足循环条件并不知道,此时使用 while 就比较合适了
#include <stdio.h>
int main() {
// 比如现在想看看i不断除以2得到的结果会是什么,但是循环次数并不明确
int i = 100;
// 现在唯一知道的是循环条件,只要大于0那么就可以继续除
while (i > 0) {
printf("%d ", i);
// 每次循环都除以2
i /= 2;
}
}
100 50 25 12 6 3 1
while 也支持使用 break 和 continue 来进行循环的控制:
#include <stdio.h>
int main() {
int i = 100;
while (i > 0) {
if (i < 30) {
break;
}
printf("%d ", i);
i /= 2;
}
}
100 50
也可以反转循环判断的位置,可以先执行循环内容,然后再做循环条件判断,这里要用到 do-while 语句:
#include <stdio.h>
int main() {
// 无论满不满足循环条件,先执行循环体里面的内容
do {
printf("Hello World!");
}
// 再做判断,如果判断成功,开启下一轮循环,否则结束
while (0);
}
Hello World!
实战:寻找水仙花数
水仙花数(Narcissistic number)也被称为超完全数字不变数(pluperfect digital invariant, PPDI)、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数(Armstrong number)。
水仙花数是指一个 3 位数,它的每个位上的数字的 3 次幂之和等于它本身。例如:13 + 53+ 33 = 153
现在设计一个 C 语言程序,打印出所有 1000 以内的水仙花数。
#include <stdio.h>
// 打印水仙花数
// 水仙花数是指一个 3 位数,它的每个位上的数字的 3 次幂之和等于它本身。例如:13 + 53+ 33 = 153
int main() {
// 因为水仙花数要求三位数,所以循环范围为100~1000
for (int i = 100; i < 1000; i++) {
// 获取百位
int hundred = i / 100;
// 获取十位
int ten = i / 10 % 10;
// 获取个位
int bit = i % 10;
// 百位、十位、个位的3次幂相加
int result = hundred * hundred * hundred + ten * ten * ten + bit * bit * bit;
// 如果百位、十位、个位的3次幂相加的结果与该数相等,即为水仙花数,打印出来
if (result == i) {
printf("%d ", i);
}
}
}
153 370 371 407
实战:打印九九乘法表
现在我们要做的是在我们的程序中,也打印出这样的一个乘法表出来,现在设计一个 C 语言程序来实现它。
#include <stdio.h>
// 打印九九乘法表
int main() {
// 第一个乘数
for (int i = 1; i <= 9; i++) {
// 第二个乘数
for (int j = 1; j <= 9; j++) {
if (i < j) {
break;
}
// %d:数字
// %2d:占两位的数字
// %-2d:占两位且靠左的数字
// \t:Tab缩进
printf("%dx%d=%-2d\t", j, i, i * j);
}
printf("\n");
}
}
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
实战:斐波那契数列解法其一
斐波那契数列(Fibonacci sequence)又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为兔子数列。
它是这样一个数列:1、1、2、3、5、8、13、21、34、……
在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N)
在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。
斐波那契数列:1,1,2,3,5,8,13,21,34,55,89...,不难发现一个规律,实际上从第三个数开始,每个数字的值都是前两个数字的和。
现在设计一个 C 语言程序,可以获取斐波那契数列上任意一位的数字,比如获取第 7 个数,那么就是 13。
#include <stdio.h>
// 获取斐波那契数列上任意一位的数字
// 1,1,2,3,5,8,13,21,34,55,89...,从第三个数开始,每个数字的值都是前两个数字的和
int main() {
// 要获取的数
int target = 7;
// 数字1
int num1 = 0;
// 数字2
int num2 = 1;
// 结果
int result = num1 + num2;
for (int i = 0; i < target - 1; i++) {
result = num1 + num2;
num1 = num2;
num2 = result;
}
printf("%d", result);
}
13