6、循环结构:while、do-while、for、循环嵌套、break与continue

循环,说白了就是让计算机反复干同一件事。你想想看,如果让你手动输出1到100,你肯定烦死了。但计算机不会烦,它只会乖乖听你的话。这就是循环存在的意义。

我个人习惯把循环分成三类:先判断再执行的while、先执行再判断的do-while、以及计数型的for。每种都有它的脾气,用错了地方,代码就会变得别扭。

6.1 while循环——先看门再进门

while的语法很简单:

while (条件表达式) {
    // 循环体
}

每次循环开始前,先检查条件。条件为真,就进去执行一次;执行完再回来检查。条件为假,直接跳过。

我在项目中遇到过一个问题:用while循环读取文件,结果文件指针没更新,直接死循环了。CPU飙到100%,服务器差点挂掉。嗯,这里要注意——循环体内一定要有改变条件的语句,否则就是死循环。

核心要点:while适合"不知道要循环多少次,但知道什么时候停"的场景。比如等待用户输入、读取数据流。

6.2 do-while循环——先进门再看门

do-while和while的区别就一句话:至少执行一次

do {
    // 循环体
} while (条件表达式);

先执行一次循环体,再判断条件。条件为真就继续,为假就退出。

什么时候用do-while?我举个例子:你写一个菜单程序,让用户选择操作。不管用户选什么,菜单至少得显示一次吧?这时候do-while就派上用场了。

小技巧:do-while在实际项目中用得不多,但一旦用上,往往是因为while解决不了"至少执行一次"的需求。别硬用,也别不用。

6.3 for循环——计数器的王者

for循环是我用得最多的。它的结构很清晰:

for (初始化; 条件; 更新) {
    // 循环体
}

三个部分各司其职:

  • 初始化:循环开始前执行一次,通常用来定义计数器
  • 条件:每次循环前检查,真则继续,假则退出
  • 更新:每次循环结束后执行,通常用来改变计数器

举个例子,遍历数组:

int arr[] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
    cout << arr[i] << endl;
}

我个人习惯把for循环用在"明确知道循环次数"的场景。比如遍历数组、处理固定长度的数据。

注意:for循环的三个部分都可以省略,但分号不能省。写成for(;;)就是死循环。我曾经在代码审查时看到有人这么写,还忘了加break,结果...你懂的。

6.4 循环嵌套——循环里面套循环

循环嵌套,说白了就是一个循环的循环体里,又有一个循环。最常见的就是处理二维数组:

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

外层循环控制行,内层循环控制列。这个模式你以后会经常见到。

我在项目中遇到过一个问题:三层循环嵌套,数据量一大,程序直接卡死。后来一分析,时间复杂度是O(n³),数据量上万就扛不住了。所以我的建议是——嵌套层数尽量控制在两层以内,超过三层就要考虑优化了。

性能提醒:循环嵌套的时间复杂度是相乘的关系。两层循环是O(n²),三层是O(n³)。数据量大的时候,差别是几何级的。

6.5 break与continue——循环的刹车和跳过

这两个关键字,一个用来提前退出,一个用来跳过本次

break:直接跳出当前循环,不再执行后续的迭代。

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;  // i等于5时直接退出循环
    }
    cout << i << " ";
}
// 输出:0 1 2 3 4

continue:跳过本次循环的剩余部分,直接进入下一次迭代。

for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        continue;  // 偶数跳过,不输出
    }
    cout << i << " ";
}
// 输出:1 3 5 7 9

我曾经在写网络通信模块时,用break来处理超时。如果规定时间内没收到响应,直接break跳出等待循环,避免程序卡死。continue则常用于过滤数据——比如处理一批数据时,跳过不合法的记录。

经验之谈:break和continue只作用于当前层的循环。如果你在嵌套循环里想跳出外层,需要用goto或者单独设置标志变量。我个人不太推荐goto,但偶尔用一次也不是什么大罪。

6.6 知识体系总览

下面这张图,把本章的核心逻辑串起来了:

循环结构知识体系 C++ 循环结构 while 循环 for 循环 do-while 循环 先判断,后执行 可能一次都不执行 初始化/条件/更新 适合计数场景 先执行,后判断 至少执行一次 循环嵌套 外层循环 + 内层循环,时间复杂度相乘 break continue 退出整个循环 跳过本次迭代

6.7 避坑指南

最后,我把自己踩过的坑总结一下:

常见错误 后果 解决方法
忘记更新循环变量 死循环,CPU 100% 检查循环体内是否有改变条件的语句
循环嵌套过深 性能急剧下降 尽量控制在两层以内,或改用其他算法
break/continue用错层 逻辑错误,难以排查 明确break/continue只作用于当前循环
for循环分号位置错 编译错误或死循环 记住for(;;)三个部分,分号不能少

我曾经在写一个数据处理模块时,三层循环嵌套,中间还用了continue。结果调试了一下午,才发现continue跳到了内层循环,而不是外层。从那以后,我写嵌套循环时都会格外小心,必要时用注释标明每一层的作用。

好了,循环这块就讲到这里。记住一句话:循环是工具,不是目的。用对了,代码简洁高效;用错了,代码又臭又长。多写多练,慢慢就有感觉了。


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