Menu

C 语言 do-while 循环:语法、示例和适用场景

C 语言中先执行循环体的循环:do-while 如何保证在判断条件之前至少执行一次,为什么结尾要加分号,它专为哪些菜单和重试场景而生,以及它和普通 while 循环有什么不同。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

至少执行一次的循环

while 循环先判断再行动,所以可能一次都不执行。这通常是对的,但有时恰恰不对:你得先把提示显示出来,才知道要不要再显示一次;你得先读到用户的输入,才能判断它是否合格。do-while 把判断移到了末尾,所以循环体先执行,条件只决定是否再来一次。

条件 i < 5 从一开始就是假的。while 循环执行零次,do-while 恰好执行一次。这就是两者的全部区别。

语法,以及那个分号

do {
    // body - always runs at least once
} while (condition);

它的书写顺序和执行顺序一致:执行(do)循环体,只要(while)条件仍然成立,就再执行一次。

右括号后面的分号是必需的。do ... while (cond); 是一条语句,而 C 语言的语句以分号结尾。漏掉它,编译器就会继续往下读,然后在下面某处报告语法错误。这也是为什么真正的错误常常离报错所指的那一行有好几行远。

这也是 C 语言中唯一一处 while (cond); 带分号是正确写法而不是 bug 的地方。在普通的 while 循环中,同样的标点会产生一个空循环体,通常还会造成死循环;而在这里,它是在结束一个 do。

循环体只有一条语句时可以省略大括号,但和 C 语言的其他控制结构一样,最好还是写上。

输入校验写法

这正是 do-while 的设计初衷。你必须先读取一个值,才能判断它是否可以接受,所以读取必须发生在判断之前:

循环一直读取,直到某个值通过检查;拒绝错误值的条件,也正是要求再执行一次的条件。如果写成 while,读取操作就得出现两次:一次在循环之前,为条件做准备;一次在循环体末尾。do-while 消除的正是这种重复。

对于真实输入,循环体会改为调用 scanf:

int age;
do {
    printf("Enter your age (1-120): ");
    if (scanf("%d", &age) != 1) {
        while (getchar() != '\n') { }   /* discard the bad line */
        age = -1;                        /* force another pass */
    }
} while (age < 1 || age > 120);

注意里面那个用来清空本行剩余内容的 while。如果 scanf 转换失败,出错的文本还留在输入缓冲区里,之后每次读取都会卡在它上面,结果就是一个不停打印提示的死循环。把它清掉是这个写法的一部分。

菜单写法

另一个经典用法:显示选项,读取选择,执行对应操作,然后重复,直到用户退出。菜单必须在有选择可以判断之前就显示出来。

switch 里的 break 结束的是 switch,而不是循环。这一点值得说清楚,因为两者用的是同一个关键字。这里循环之所以结束,是因为 choice != 4 变成了假。如果你确实想在 switch 内部跳出循环,可以用 return 或一个标志变量;单靠 break 做不到。

保证执行一次也可能是 bug

因为循环体在任何判断之前就执行,do-while 会毫不犹豫地处理根本不存在的数据:

/* BUG: prints a[0] even when n is 0 */
int i = 0;
do {
    printf("%d\n", a[i]);
    i++;
} while (i < n);

当 n == 0 时,这段代码会在空数组上读取 a[0],这是未定义行为。任何遍历可能为空的集合的循环,都应该用 while 或 for,它们先进行判断。

经验法则:只有当执行零次从问题本身来看就不可能,而不只是实际中不太会发生时,才使用 do-while。提示用户输入、掷骰子直到出现目标点数、生成候选值再检验,这些确实无法在第一次执行之前做出判断。遍历一个列表则不属于这种情况。

do-while 中的 break 和 continue

两者都可以用,而 continue 有一个值得了解的细节:它跳到的是条件判断,而不是循环体的开头。

循环体开头的 i++ 每一轮都会执行,包括被 continue 提前结束的那些轮次,所以循环仍然会向前推进。如果把更新语句放在 continue 之后,循环就会卡住,这和 while 循环的陷阱一样。

break 会立即离开循环,完全跳过条件判断。

三种循环怎么选

C 语言的三种循环能力相同,选哪一种取决于哪一种最能表达你的意图。

for        a counter, its limit, and its step belong together - "n times"
while      repeat until a condition changes, and maybe not at all - "until"
do-while   the same, but the first pass is guaranteed - "at least once"

实际上,do-while 是三者中用得最少的,而且少得多。大多数循环要么是计数,要么需要处理集合为空的情况,这两种都需要把判断放在开头。当你发现自己在 while 循环前面重复写了一条语句,只是为了给条件做准备时,就该考虑 do-while 了:这种重复就是信号。

常见问题

C 语言中 do-while 循环怎么写?

do { body } while (condition);:循环体在前,判断在后,整条语句以分号结尾。循环体至少执行一次,之后只要条件不为零就继续重复。

C 语言中 while 和 do-while 有什么区别?

区别在于判断的位置。while 循环在第一次执行前就检查条件,所以可能一次都不执行。do-while 在执行之后才检查,所以至少执行一次。只有当问题确实需要这个“保证执行一次”时,才选用 do-while。

为什么 do-while 循环末尾需要分号?

因为 do ... while (cond); 是一条语句,和 C 语言中的其他语句一样,要用分号结束。漏掉分号会得到一个令人困惑的语法错误,指向的是后面的某一行,因为编译器会继续往下读,寻找这条语句的剩余部分。

什么时候应该用 do-while 循环?

当你必须先执行循环体,才能知道是否需要重复时:先显示菜单再读取选择,在输入合法之前反复提示,或者先生成一个值再检验它。如果条件可以在一开始就检查,用 while 循环 表达得更直接。

Coddy 编程语言插图

和 Coddy 一起学编程

开始