11、循环控制:break跳出循环,continue跳过本次循环,goto语句的慎用

循环控制,说白了就是让循环听话。你写了一个循环,有时候需要提前结束,有时候需要跳过某一次,有时候甚至想直接跳到程序的某个位置。这三种需求,对应着三个关键字:breakcontinuegoto

我个人习惯是,能用 breakcontinue 解决的问题,绝不用 goto。为什么?咱们一个一个说。

11.1 break:跳出整个循环

break 的作用很简单——立即终止当前所在的循环,程序继续执行循环后面的代码。

我在项目中遇到过这样一个场景:一个传感器采集数据,正常情况下采集100次,但如果采集到异常值(比如超过阈值),就必须立即停止采集,不能再继续了。这时候 break 就派上了用场。

#include <stdio.h>

int main() {
    int data[] = {23, 45, 67, 89, 120, 34, 56};
    int i;
    
    for (i = 0; i < 7; i++) {
        if (data[i] > 100) {
            printf("检测到异常值:%d,停止采集!\n", data[i]);
            break;  // 跳出循环
        }
        printf("采集数据:%d\n", data[i]);
    }
    
    printf("循环结束,共采集了%d次\n", i + 1);
    return 0;
}

运行结果:

采集数据:23
采集数据:45
采集数据:67
采集数据:89
检测到异常值:120,停止采集!
循环结束,共采集了5次

你看,当 data[4] 等于120时,break 直接让循环终止了,后面的34和56根本没机会被处理。

注意: break 只跳出它所在的那一层循环。如果你有嵌套循环,break 只跳出内层循环,外层循环继续执行。

11.2 continue:跳过本次循环

continuebreak 不一样。它不会终止循环,而是跳过当前这一次迭代,直接进入下一次。

嗯,这里要注意:continue 后面的代码不会执行,但循环条件判断和迭代更新照常进行。

我曾经写过一个日志分析程序,需要统计有效数据的数量。那些空行或者注释行,直接用 continue 跳过去,不参与统计。

#include <stdio.h>

int main() {
    int numbers[] = {10, -1, 20, -1, 30, 40, -1, 50};
    int sum = 0;
    int count = 0;
    int i;
    
    for (i = 0; i < 8; i++) {
        if (numbers[i] == -1) {
            continue;  // 跳过无效数据
        }
        sum += numbers[i];
        count++;
    }
    
    printf("有效数据个数:%d\n", count);
    printf("有效数据总和:%d\n", sum);
    return 0;
}

运行结果:

有效数据个数:5
有效数据总和:150

你想想看,如果没有 continue,你得写一个 if-else 把累加操作包起来,代码就多了一层缩进,可读性也差一些。

小技巧:whiledo-while 循环中使用 continue 时要小心。如果 continue 跳过了循环变量的更新语句,可能导致死循环。我建议在 for 循环中使用 continue,因为它的更新操作在循环头部,不会被跳过。

11.3 goto:能不用就不用

goto 是C语言里一个争议很大的关键字。它可以让程序跳转到任意一个标号(label)处执行。听起来很自由,但自由过了头就是灾难。

为什么这么说?goto 会破坏程序的结构化。你跳来跳去,代码的执行流程变得像一团乱麻,别人看不懂,你自己过两周也看不懂。

我曾经接手过一个老项目,里面用了大量的 goto。调试的时候,我根本不知道程序下一步会跳到哪。那感觉,就像在迷宫里走,到处是死胡同。

#include <stdio.h>

int main() {
    int error = 0;
    
    // 模拟一些操作
    if (/* 某个条件 */) {
        error = 1;
        goto cleanup;  // 跳转到清理代码
    }
    
    // 更多操作...
    
cleanup:
    if (error) {
        printf("发生错误,执行清理\n");
    }
    printf("程序结束\n");
    return 0;
}

上面这个例子,用 goto 做错误处理,看起来还行。但如果你在代码里到处乱跳,那就麻烦了。

核心原则: goto 只在两种情况下可以考虑使用:
  • 从多层嵌套循环中一次性跳出(用 break 需要写很多层判断)
  • 统一的错误处理和资源清理(比如释放内存、关闭文件)
除此之外,能用 breakcontinue 解决的问题,就别碰 goto

11.4 三种控制语句对比

关键字 作用 对循环的影响 使用频率
break 终止循环 整个循环结束
continue 跳过本次迭代 进入下一次迭代
goto 跳转到指定位置 可跳出循环,也可跳入循环 极低

11.5 知识体系图

下面这张图帮你理清这三种控制语句的关系和使用场景:

循环控制语句知识体系 循环控制语句 break continue goto(慎用) 终止整个循环 只跳出当前层循环 跳过本次迭代 进入下一次迭代 跳转到任意位置 破坏结构化流程 建议:优先使用 break 和 continue,避免使用 goto

11.6 避坑指南

我曾经在调试一个电机控制程序时,遇到一个诡异的bug。电机转着转着突然停了,但程序还在跑。查了半天,发现是 break 用在了 switch 语句里,而那个 switch 又嵌套在 for 循环里。结果 break 只跳出了 switch,没跳出 for 循环。嗯,这里要注意:breakswitch 里只跳出 switch,不会跳出外层循环。

还有一次,我在一个 while 循环里用了 continue,但循环变量的更新写在循环体末尾。结果 continue 一跳,更新语句就被跳过了,直接死循环。从那以后,我养成了一个习惯:while 循环里尽量不用 continue,或者把更新语句放在 continue 之前。

关于 goto 的忠告: 如果你觉得自己非用 goto 不可,先停下来想一想:能不能用 break 加标志变量解决?能不能把代码重构一下?能不能用函数封装?如果这三个问题都回答「不能」,那再用 goto。即便如此,也要确保 goto 只向前跳,不向后跳,避免形成循环。

说白了,breakcontinue 是工具,goto 是双刃剑。工具要常用,双刃剑要慎用。你想想看,写代码是为了让人看懂,不是为了炫技。代码清晰、逻辑简单,才是真正的本事。


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