8、流程控制(二):while循环、do-while循环、for循环、循环嵌套、break与continue

循环,说白了就是让计算机反复干同一件事。你想想看,如果让你写一个程序,把1加到100,没有循环的话,你得写100行加法。有了循环,三行代码搞定。这就是循环存在的意义。

我个人习惯把循环分成三类:whiledo-whilefor。它们都能实现重复执行,但适用场景各有不同。今天咱们就把它们彻底讲透。

C语言循环结构总览 while 循环 do-while 循环 for 循环 先判断条件 条件为真才执行 可能一次都不执行 先执行一次 再判断条件 至少执行一次 初始化+条件+更新 三合一 最常用 break 提前跳出 · continue 跳过本次 循环嵌套:内层循环跑完一轮,外层才走一步

8.1 while 循环:先看条件再干活

while 循环的语法很简单:

while (条件表达式) {
    // 循环体
    // 当条件为真时反复执行
}

它的执行逻辑是:先判断条件,条件为真才进入循环体。如果一开始条件就是假,循环体一次都不执行。

核心要点:while 循环适合「不知道具体循环次数,只知道什么时候停止」的场景。

举个例子,从键盘读取用户输入,直到输入 'q' 才退出:

char ch;
while ((ch = getchar()) != 'q') {
    printf("你输入了: %c\n", ch);
}

我在项目中遇到过类似场景:读取配置文件,一行一行读,直到文件末尾。用 while 循环配合 feof() 判断,非常自然。

⚠ 死循环陷阱:如果条件永远为真,循环就永远不会结束。我曾经调试过一个服务器程序,发现 CPU 占用 100%,查了半天,原来是 while 循环里忘了更新循环变量。嗯,这种错误新手最容易犯。

8.2 do-while 循环:先干一次再说

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

do {
    // 循环体
    // 先执行一次,再判断条件
} while (条件表达式);  // 注意这里有分号!

你想想看,什么时候需要「先执行一次再说」?比如菜单程序:先显示菜单,再让用户选择,选错了就重新显示。第一次显示菜单不需要任何条件。

int choice;
do {
    printf("1. 新建文件\n");
    printf("2. 打开文件\n");
    printf("3. 退出\n");
    printf("请选择: ");
    scanf("%d", &choice);
} while (choice < 1 || choice > 3);

我个人习惯在需要「至少执行一次」的场景下使用 do-while。比如游戏主循环、交互式输入校验。while 和 do-while 的选择其实很简单:如果循环体可能一次都不需要执行,用 while;如果无论如何都要执行一次,用 do-while。

💡 小技巧:do-while 后面的分号经常被漏掉。我刚开始学 C 的时候,因为这个分号被编译器报错过不下十次。养成习惯:写完 do-while 立刻补上分号。

8.3 for 循环:最常用的循环

for 循环把「初始化」「条件判断」「循环变量更新」三件事写在一行,结构清晰:

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

比如从 1 加到 100:

int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum += i;
}
printf("1+2+...+100 = %d\n", sum);

为什么 for 循环最常用?因为它把循环的三个关键要素集中在一起,一眼就能看出循环的范围和步长。我个人在写数组遍历、字符串处理、数值计算时,90% 的情况都用 for 循环。

注意:C99 标准之后,可以在 for 的初始化部分声明变量(如 int i = 1),这个变量的作用域仅限于循环内部。老标准不支持,如果你在嵌入式环境或老旧编译器上开发,要注意这一点。

for 循环的三个部分都可以省略,但分号不能省:

// 无限循环
for (;;) {
    // 相当于 while(1)
}

这种写法在服务器程序、事件循环中很常见。我见过不少老代码用 for(;;) 表示无限循环,比 while(1) 更「地道」。

8.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++) {
        printf("%3d ", matrix[i][j]);
    }
    printf("\n");
}

执行顺序是这样的:外层 i=0 时,内层 j 从 0 跑到 3;然后外层 i=1,内层 j 再跑一轮……说白了,内层循环跑完完整的一轮,外层才走一步。

⚠ 性能陷阱:循环嵌套的层数越多,执行次数呈指数增长。三层嵌套,每层 100 次,就是 100 万次。我曾经接手过一个图像处理模块,里面写了五层循环嵌套,处理一张 1024×768 的图片要好几秒。优化成矩阵运算后,速度提升了 50 倍。所以,嵌套层数尽量控制在三层以内。

8.5 break 与 continue:控制循环的「刹车」和「跳过」

这两个关键字用来在循环内部改变执行流程:

关键字 作用 类比
break 立即跳出当前循环 刹车——直接停车
continue 跳过本次循环剩余代码,进入下一次 跳过——这轮不干了

看个例子:查找数组中第一个负数:

int arr[] = {3, 7, -2, 9, 1, -5, 8};
int found = -1;

for (int i = 0; i < 7; i++) {
    if (arr[i] < 0) {
        found = i;
        break;  // 找到第一个负数就跳出循环
    }
}

if (found != -1) {
    printf("第一个负数在位置 %d\n", found);
}

continue 的用法:打印 1 到 10 中所有偶数:

for (int i = 1; i <= 10; i++) {
    if (i % 2 != 0) {
        continue;  // 奇数跳过,不打印
    }
    printf("%d ", i);
}

💡 我的经验:break 和 continue 只作用于它们所在的最内层循环。如果你在嵌套循环里想跳出外层,需要用 goto 或者设置标志变量。我个人不太推荐用 goto,但某些场景下(比如多层循环异常退出),goto 反而让代码更清晰。嗯,这个咱们后面再细聊。

8.6 三种循环的选择指南

说了这么多,到底什么时候用哪个?我总结了一张表:

场景 推荐循环 原因
知道确切循环次数 for 初始化、条件、更新写在一起,一目了然
不知道次数,只知道停止条件 while 条件驱动,灵活
至少需要执行一次 do-while 先执行后判断,保证至少一次
遍历数组/字符串 for 下标控制方便,代码简洁
读取文件直到末尾 while feof() 判断自然
菜单/交互式输入 do-while 先显示菜单再判断是否继续

其实说白了,三种循环可以互相转换。你完全可以用 while 实现 for 的功能,反之亦然。但好的程序员会选择最贴合场景的那一种——代码是写给人看的,可读性比炫技重要得多。

避坑指南:我曾经在项目里见过一个 bug,循环条件里用了赋值号 = 而不是等号 ==。比如 while (x = 5) 永远为真,因为赋值表达式的值就是 5(非零)。这种错误编译器不会报错,但运行结果完全不对。我的习惯是:把常量写在左边,比如 while (5 == x),这样万一写成 = 编译器会报错。嗯,这个小技巧救过我很多次。

循环是 C 语言的基石之一。掌握了 while、do-while、for 以及 break 和 continue,你就能写出大部分逻辑了。多写、多练、多踩坑,这些知识点才能真正变成你自己的东西。


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