第31章 do-while 循环:它和 while 循环的根本区别

说实话,很多学C语言的朋友,写了好几年代码,对 do-while 还是有点「敬而远之」。我见过不少项目里,明明用 do-while 更合适,大家却硬生生写成 while + 额外标记变量。为什么?因为没搞懂它俩的根本区别。

今天咱们就把这个事彻底说清楚。

31.1 先看一个最直观的例子

先上代码,你感受一下:

// while 循环
int x = 10;
while (x < 5) {
    printf("x = %d\n", x);
    x++;
}
// 输出:无任何输出

// do-while 循环
int y = 10;
do {
    printf("y = %d\n", y);
    y++;
} while (y < 5);
// 输出:y = 10

看到了吗?条件不满足时,while 一次都不执行,do-while 至少执行一次。

这就是根本区别——判断时机不同

  • while:先判断,后执行。条件为假,循环体直接跳过。
  • do-while:先执行,后判断。循环体至少跑一次。

核心一句话:do-while 保证循环体至少执行一次,while 不保证。

31.2 什么时候该用 do-while?

我个人习惯是:当循环体必须执行一次,且执行结果影响判断条件时,用 do-while。

举个实际例子。我在做嵌入式项目时,经常需要等待某个硬件寄存器变成特定值。比如等待 ADC 转换完成:

// 等待 ADC 转换完成
do {
    status = read_adc_status();  // 先读一次状态
} while (status & ADC_BUSY);     // 如果还在忙,继续等

为什么这里用 do-while?因为 你总得先发起一次转换,才能知道它完没完成。如果用 while,你得在循环外面先读一次状态,代码就多了一行冗余:

status = read_adc_status();      // 冗余的第一次读取
while (status & ADC_BUSY) {
    status = read_adc_status();
}

你看,do-while 把第一次读取自然地放进了循环体里,代码更干净、更符合逻辑。

31.3 另一个经典场景:用户输入验证

我记得有一次写一个菜单程序,要求用户输入 1-3 之间的数字。用 do-while 简直不要太舒服:

int choice;
do {
    printf("请选择 (1-3): ");
    scanf("%d", &choice);
} while (choice < 1 || choice > 3);

用户至少得输入一次,然后根据输入决定要不要继续提示。如果用 while,你得先给 choice 赋一个非法值才能进入循环,多此一举。

小技巧:凡是「先做一次,再决定是否继续」的场景,优先考虑 do-while。比如:读取文件直到末尾、发送命令直到收到响应、轮询设备状态等。

31.4 两者的流程图对比

下面这张 SVG 图,把 while 和 do-while 的执行流程画得清清楚楚。你看一眼就明白了。

while 循环 条件为真? 执行循环体 退出循环 do-while 循环 执行循环体 条件为真? 退出循环

从图上可以清楚看到:while 是先经过判断菱形再进循环体,do-while 是先执行循环体再判断。这个顺序差异,决定了它们的使用场景完全不同。

31.5 避坑指南

我曾经在一个通信协议栈里犯过这个错:用 while 循环等待接收缓冲区非空,结果第一次调用时缓冲区本来就是空的,循环直接跳过,导致后续逻辑全部跑飞。后来改成 do-while,问题立刻解决。

这里总结几个常见的坑:

  1. 不要忘了 do-while 后面的分号。while 后面没有分号,do-while 的 while 后面有分号。这个我见过无数人漏掉。
  2. do-while 的循环体即使只有一条语句,也建议加花括号。虽然语法允许不加,但加了可读性更好,也避免以后扩展时出错。
  3. 注意 do-while 和 while 在逻辑上的等价转换。有时候你看到别人的代码用 while 实现了「至少执行一次」的效果,那多半是在循环外面先执行了一次——这种写法不如 do-while 直观。

31.6 什么时候用 while 而不是 do-while?

反过来想,什么时候 不该 用 do-while?

当循环体可能一次都不需要执行时,用 while。比如遍历一个可能为空的链表:

Node *p = head;
while (p != NULL) {
    process(p);
    p = p->next;
}

如果链表是空的,p 一开始就是 NULL,循环体一次都不执行——这正是我们想要的。如果用 do-while,就会先处理一个空节点,直接崩溃。

我的建议:写代码前先问自己一个问题——「这个循环体有没有可能一次都不该执行?」如果答案是「有可能」,用 while;如果答案是「必须至少执行一次」,用 do-while。

31.7 总结一下

对比项 while do-while
判断时机 先判断,后执行 先执行,后判断
最少执行次数 0 次 1 次
典型场景 遍历链表、搜索数据 用户输入验证、硬件轮询
语法特点 while 后无分号 while 后有分号

说白了,do-while 就是 while 的「先干再说」版本。你想想看,生活中很多事情也是这个道理——先做一次尝试,再决定要不要继续。do-while 恰恰反映了这种思维模式。

嗯,这个区别其实很简单,但很多人就是没转过这个弯。希望今天讲完,你能彻底记住:do-while 保证至少执行一次,while 不保证。就这么简单。


公众号:蓝海资料掘金营,微信 deep3321