第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循环 先判断条件,后执行循环体 可能一次都不执行 适用于条件控制严格的场景 先执行循环体,后判断条件 至少执行一次 适用于菜单、输入验证等场景 循环嵌套:内外层循环组合使用

嗯,while和do-while就这些内容。记住核心区别:while可能一次都不执行,do-while至少执行一次。选哪个,看你的场景。

专注资料整理