14. 循环结构(下):for循环、循环控制、无限循环与优化技巧

上一章我们聊了 while 和 do-while,今天来把循环剩下的内容一口气讲完。for 循环、break、continue、无限循环,还有我这些年积累的一些优化小窍门。嗯,这些内容在实际项目中几乎天天都要打交道。

14.1 for 循环:最常用的循环结构

说实话,for 循环是我用得最多的循环。为什么?因为它把初始化、条件判断、循环更新都写在一行里,结构清晰,不容易漏东西。

// 标准 for 循环语法
for (初始化表达式; 条件表达式; 更新表达式) {
    循环体语句;
}

来看个实际例子。我在做数据采集项目时,经常需要遍历数组:

#include <stdio.h>

int main() {
    int sensor_data[5] = {23, 45, 67, 12, 89};
    
    // 遍历数组,找出最大值
    int max = sensor_data[0];
    for (int i = 1; i < 5; i++) {
        if (sensor_data[i] > max) {
            max = sensor_data[i];
        }
    }
    
    printf("最大值是:%d\n", max);
    return 0;
}

我个人习惯把循环变量 i 定义在 for 语句内部,这样它的作用域就限制在循环里,不会污染外部命名空间。你想想看,要是整个函数里到处都是 i、j、k,调试起来多头疼。

小技巧:for 循环的三个表达式都可以省略,但分号不能少。比如 for(;;) 就是一个无限循环,后面会讲到。

14.2 循环控制:break 和 continue

这两个关键字,说白了就是给循环装了个「急刹车」和「跳过键」。

14.2.1 break:跳出整个循环

break 用来提前终止循环。我在项目中遇到过这样一个场景:从传感器读取数据,读到异常值就立刻停止处理。

#include <stdio.h>

int main() {
    int readings[] = {12, 15, 18, 99, 20, 22};
    
    for (int i = 0; i < 6; i++) {
        if (readings[i] > 50) {
            printf("检测到异常值 %d,停止读取!\n", readings[i]);
            break;  // 跳出循环
        }
        printf("正常读数:%d\n", readings[i]);
    }
    
    return 0;
}

输出结果:

正常读数:12
正常读数:15
正常读数:18
检测到异常值 99,停止读取!

14.2.2 continue:跳过本次循环

continue 和 break 不一样。它不跳出循环,只是跳过当前这一次,继续下一次。我曾经在写数据过滤程序时用过它:

#include <stdio.h>

int main() {
    int data[] = {3, -1, 5, 0, 7, -2, 9};
    
    printf("过滤后的有效数据:");
    for (int i = 0; i < 7; i++) {
        if (data[i] <= 0) {
            continue;  // 跳过非正数
        }
        printf("%d ", data[i]);
    }
    printf("\n");
    
    return 0;
}

输出结果:

过滤后的有效数据:3 5 7 9
注意:在嵌套循环中,break 和 continue 只作用于它们所在的最内层循环。我曾经见过新手用 break 想跳出两层循环,结果只跳出了一层,bug 找了半天。

14.3 无限循环:什么时候用?怎么用?

无限循环,就是条件永远为真的循环。你可能会问:「这不会让程序卡死吗?」其实不会,只要我们在循环体里放一个 break 条件就行。

我常用的两种写法:

// 写法一:for 无限循环
for (;;) {
    // 循环体
    if (退出条件) {
        break;
    }
}

// 写法二:while 无限循环(我个人更常用这种)
while (1) {
    // 循环体
    if (退出条件) {
        break;
    }
}

为什么需要无限循环?举个真实例子。我在做嵌入式设备时,主程序就是一个无限循环:

#include <stdio.h>
#include <unistd.h>  // sleep 函数

int main() {
    while (1) {
        // 1. 读取传感器数据
        int temp = read_temperature();
        
        // 2. 判断是否超温
        if (temp > 80) {
            printf("温度过高!触发报警!\n");
            trigger_alarm();
        }
        
        // 3. 等待 1 秒再检测
        sleep(1);
    }
    
    return 0;
}

你看,设备上电后就要一直运行,直到断电。这种场景用无限循环再合适不过了。

14.4 循环的优化技巧

做嵌入式开发,资源有限,循环优化是基本功。下面这几个技巧,都是我在实际项目中踩过坑之后总结出来的。

14.4.1 减少循环内的函数调用

把不变的表达式提到循环外面:

// ❌ 低效写法
for (int i = 0; i < strlen(str); i++) {
    // 每次循环都调用 strlen,O(n²)
}

// ✅ 高效写法
int len = strlen(str);
for (int i = 0; i < len; i++) {
    // 只调用一次 strlen,O(n)
}

14.4.2 使用前置递增

在 C++ 里,++i 比 i++ 效率高。C 语言中差别不大,但我习惯用 ++i:

// 推荐写法
for (int i = 0; i < 100; ++i) {
    // ...
}

14.4.3 循环展开

对于循环次数固定的简单操作,可以手动展开,减少循环开销:

// 原始循环
for (int i = 0; i < 4; i++) {
    a[i] = b[i] + c[i];
}

// 循环展开
a[0] = b[0] + c[0];
a[1] = b[1] + c[1];
a[2] = b[2] + c[2];
a[3] = b[3] + c[3];
我的建议:不要过早优化。先把功能写对,再用性能分析工具找出热点,最后针对性地优化。我曾经为了省几个时钟周期把代码改得乱七八糟,结果 bug 一堆,得不偿失。

14.5 知识体系总览

下面这张图把本章的核心内容串起来了,你可以对照着复习:

循环结构(下)知识体系 for 循环 for(初始化;条件;更新) break:跳出循环 continue:跳过本次 无限循环 for(;;) / while(1) 优化技巧 • 减少循环内函数调用 • 使用前置递增 ++i • 循环展开

14.6 避坑指南

最后,分享几个我踩过的坑:

  • 循环变量溢出:我曾经用 unsigned char 做循环变量,结果到 255 后变成 0,死循环了。记得选对数据类型。
  • break 只跳一层:嵌套循环里想跳出多层?用 goto 或者加一个标志变量。
  • continue 的位置:如果 continue 在循环尾之前,后面的代码会被跳过。我见过有人把资源释放写在 continue 后面,结果内存泄漏了。

好了,for 循环的内容就讲到这里。记住:多写、多练、多调试,循环这块就能拿捏得死死的。


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