Menu

C 语言 break 和 continue:用法与适用场景

break 跳出循环或 switch,continue 跳到下一轮。本文讲解它们在 for、while 和 do-while 中的行为,如何用标志变量或 goto 跳出嵌套循环,以及让循环难以读懂的常见误用。

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

改变循环流程的两种方式

循环的条件决定它何时结束,但有时真正的答案出现在循环体中间。你在数组里查找某个元素,第三个就找到了,何必再检查剩下的 997 个?又或者你在处理一个列表,其中一项是空的:跳过它,继续处理。

C 语言为这两种情况各准备了一个关键字:

  • break:立即离开循环。程序从循环之后的第一条语句继续执行。
  • continue:只放弃这一轮,进入下一轮。

第一个循环打印 1 2 3 4 后就停止了。第二个打印 1 2 3 4 6 7 8 9 10:它跳过了一个值,然后继续执行。

循环中的 break

最典型的用法是一找到目标就停止的查找:

因为 found 在 break 之前就被赋值了,循环之后的代码可以区分“找到了”和“元素都查完了”这两种情况。结果变量加 break 的组合,就是 C 语言中标准的查找写法。如果循环计数器是在循环头里声明的,循环结束后就不能再用它,因为它在右花括号处就离开了作用域。

break 每次只跳出一层:离它最近的 for、while、do-while 或 switch。

循环中的 continue

continue 用于那些应该跳过、但不应该中止整个处理的元素:

continue 跳到哪里,取决于循环的类型:

for        -> the update part of the header (so the counter still advances)
while      -> the condition test (nothing else in the body runs)
do-while   -> the condition test at the bottom

这个区别很实际。在 for 循环中,continue 永远不会跳过计数器自增,因为自增写在循环头里。在 while 循环中却会:

/* BUG: continue skips i++, so this hangs at the first even number */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

要么把 i++ 移到 continue 之前,要么改写成 for 循环,循环头的结构让你不可能写错。

switch 中的 break 不会跳出循环

break 结束的是离它最近的循环或 switch,看哪个更近。把 switch 放进循环里,switch 中的每个 break 都属于 switch:

q 命令打印了它的消息,但循环继续处理了 cc。如果“退出”本应结束循环,这个 break 并没有做到。解决办法有三种:标志变量、goto,或者把循环移到函数里用 return,下面会一一演示。

跳出嵌套循环

一个 break 只跳出一层。在两层嵌套循环中,它只结束内层循环,外层循环会像什么都没发生一样开始下一轮。

C 语言没有 break 2,也没有带标签的 break。有三种正当的做法:

标志变量。 可移植,一目了然,只是稍显啰嗦:

代价是标志变量要出现在两个地方:外层条件和赋值处。嵌套到三层时,这种写法就真的很难受了。

goto。 C 语言中饱受诟病的跳转语句,而这正是有经验的 C 程序员会理直气壮地使用它的场景:

向前 goto 到循环之后的标签,效果和其他语言中带标签的 break 完全一样,只用一行,也不需要维护标志变量。goto 的坏名声来自向后跳转和跳进其他代码块中间,那些确实会让控制流难以阅读。从嵌套循环中向前跳出一次并不属于这种情况。Linux 内核在清理路径中也使用同样的模式。要有意识地、节制地使用它;别因为忌讳 goto,就写出一个用三个标志变量、更难读懂的替代方案。

函数加 return。 通常是最好的答案,因为一次查找值得拥有一个名字:

return 会一次性退出函数中的所有循环,不需要标志变量,也不需要标签,而且查找代码现在有了名字,可以单独测试。当嵌套深到跳出它都成了问题时,这往往说明这段代码本来就该写成一个函数。

什么时候有帮助,什么时候有害

当 break 和 continue 能让循环在顶部一次性说明“这种情况处理完了”,而不必把整个循环体包进一个 if 时,它们就物有所值。对比一下:

/* with continue: the real work is not indented */
for (int i = 0; i < n; i++) {
    if (!isValid(a[i])) continue;
    if (isDuplicate(a[i])) continue;
    process(a[i]);
}

/* without: every guard adds a level */
for (int i = 0; i < n; i++) {
    if (isValid(a[i])) {
        if (!isDuplicate(a[i])) {
            process(a[i]);
        }
    }
}

用得太多时,它们就有害了。一个散落着五个 break 和三个 continue 的循环,它的退出条件没有写在任何地方,读代码的人必须在脑中把整个循环体模拟一遍,才知道它什么时候停止。两条经得起考验的准则:把 continue 守卫语句集中放在循环体顶部;如果一个循环有一两个以上的 break,想一想循环头里的条件是不是本该承担这份工作。

还有两个小陷阱。在 do-while 中,continue 跳到底部的条件判断,而那本来就是接下来要执行的部分:无害,但常常让人意外。另外,在任何循环或 switch 之外使用 break 是编译错误,这是本文所有错误中唯一一个编译器能替你发现的。

常见问题

C 语言中 break 和 continue 有什么区别?

break 直接结束整个循环,程序从循环之后的语句继续执行。continue 只放弃当前这一轮,进入下一轮:在 for 循环中跳到更新部分,在 while 或 do-while 中直接跳到条件判断。

C 语言怎么跳出嵌套循环?

一个 break 只能跳出最内层的循环。要同时跳出两层,可以设置一个标志变量并在外层循环的条件里检查它,可以用 goto 跳到循环之后的标签,或者把循环移到一个函数里再 return,这通常是最清晰的做法。

break 跳出的是循环还是 switch?

跳出离它最近的那一层。位于循环内部 switch 里的 break 只结束 switch,循环会继续执行。要在 switch 内部跳出循环,需要标志变量、goto 或 return。

为什么 continue 让我的 while 循环变成了死循环?

因为 continue 会跳到条件判断,跳过循环体剩下的部分,如果计数器自增写在末尾,它也会被跳过。在 for 循环 中更新部分写在循环头里,所以总会执行;在 while 循环中,把更新移到 continue 之前,或者改用 for。

Coddy 编程语言插图

和 Coddy 一起学编程

开始