第9章:循环结构之while与do-while
循环,说白了就是让计算机反复干同一件事。你想想看,如果让你写一个程序,要打印1到100的数字,难道真要写100行printf?当然不。这时候循环就派上用场了。
我个人习惯把循环分成两类:一类是「先判断再干活」,另一类是「先干一次再说」。前者是while,后者是do-while。嗯,咱们一个一个聊。
9.1 while循环:先看门再进门
while循环的语法很简单:
while (条件表达式) {
// 循环体
// 这里放要重复执行的代码
}
它的执行流程是这样的:先判断条件是否为真,如果为真就执行循环体,执行完再回来判断条件。如果条件一开始就是假,那循环体一次都不执行。
我在项目中遇到过这样一个场景:需要等待一个硬件寄存器置位,才能继续往下走。用while就很合适:
while (!(REG_STATUS & 0x01)) {
// 等待硬件就绪
// 可以加个超时保护,防止死等
}
这里要注意,如果硬件一直没就绪,程序就卡死了。所以我一般会在while循环里加个计数器做超时处理。
while循环最常见的坑就是忘记改变条件变量。比如:
int i = 0;
while (i < 10) {
printf("%d\n", i);
// 忘了写 i++,循环永远跑不完
}
我曾经调试过一个bug,就是同事在while循环里忘了更新循环变量,结果程序跑飞了。排查了整整一下午。
9.2 do-while循环:先进门再看门
do-while和while的区别就一句话:do-while至少执行一次循环体。
do {
// 循环体
// 至少执行一次
} while (条件表达式);
注意这里有个分号,很多人会漏掉。我刚开始学C语言时就犯过这个错,编译报错还一脸懵。
什么时候用do-while?我举个例子。比如你要从用户那里读一个数字,要求必须在1到100之间。用do-while就很自然:
int num;
do {
printf("请输入一个1-100之间的数字:");
scanf("%d", &num);
} while (num < 1 || num > 100);
你看,先让用户输入一次,如果不对再继续问。如果用while,就得先给num赋个初值,比如num = 0,然后才能进循环。多了一步,不优雅。
我个人习惯:如果循环体至少需要执行一次,就用do-while;如果可能一次都不执行,就用while。说白了就是看场景。
9.3 while与do-while的对比
| 特性 | while | do-while |
|---|---|---|
| 判断时机 | 先判断,后执行 | 先执行,后判断 |
| 最少执行次数 | 0次 | 1次 |
| 适用场景 | 条件控制严格,可能不需要执行 | 至少需要执行一次 |
| 语法注意 | 无分号 | while后有分号 |
9.4 循环的嵌套
循环嵌套,就是循环里面套循环。最常见的就是用两个for循环打印九九乘法表。但while和do-while也可以嵌套。
我记得有一次做嵌入式项目,需要扫描一个8x8的LED矩阵。外层循环控制行,内层循环控制列。用while嵌套写出来是这样的:
int row = 0;
while (row < 8) {
int col = 0;
while (col < 8) {
// 点亮第row行第col列的LED
led_on(row, col);
col++;
}
row++;
}
嵌套循环要注意的是:内层循环的变量不要和外层冲突。我见过有人内外都用i,结果内层把外层i改了,外层循环直接乱套。
- 内外层循环变量要不同名
- 内层循环要完整执行完,外层才会进入下一次
- 嵌套层数不要太深,超过3层代码可读性就差了
- 可以用break跳出内层循环,但不会影响外层
9.5 实战:用while实现一个简易菜单
嵌入式设备里经常要用到菜单循环。用户选一个功能,执行完再回到菜单。用do-while就很合适:
int choice;
do {
printf("===== 菜单 =====\n");
printf("1. 显示温度\n");
printf("2. 显示湿度\n");
printf("3. 退出\n");
printf("请选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("当前温度:25.3°C\n");
break;
case 2:
printf("当前湿度:60%%\n");
break;
case 3:
printf("正在退出...\n");
break;
default:
printf("无效选择,请重新输入\n");
}
} while (choice != 3);
你看,菜单至少显示一次,用户选3才退出。这就是do-while的典型用法。
9.6 避坑指南
我这些年写C代码,在循环上踩过的坑不少。挑几个典型的说说:
- 忘记更新循环变量:while(i < 10)里忘了写i++,直接死循环。我曾经在调试一个电机控制程序时,就因为少写了一行i++,电机一直转停不下来。
- 分号问题:while后面不小心加了个分号,比如
while(i < 10);,循环体就变成空语句了。这种bug很难查,因为编译不报错。 - 嵌套循环变量冲突:内外层用了同一个变量名,内层改了外层也受影响。我建议外层用i,内层用j,养成习惯。
- do-while漏分号:while后面那个分号很容易忘,编译会报错,但新手经常找不到原因。
如果怀疑循环有问题,可以在循环体里加个printf打印循环变量的值。虽然土,但管用。嵌入式里可以用串口打印。
9.7 本章知识体系
下面这张图帮你理清while和do-while的关系:
嗯,while和do-while就这些内容。记住核心区别:while可能一次都不执行,do-while至少执行一次。选哪个,看你的场景。