10、循环结构之for循环:for循环的语法、执行流程,循环嵌套,死循环
说实话,循环是C语言里最常用的结构之一。我做了十几年嵌入式开发,几乎每个项目都离不开它。不管是驱动开发、数据处理,还是状态机实现,for循环都是我的得力助手。
今天咱们就好好聊聊for循环。别小看它,用好了能写出优雅高效的代码,用不好嘛...嗯,我见过不少新手在循环上栽跟头。
10.1 for循环的语法结构
先看最基础的样子:
for (初始化表达式; 循环条件; 更新表达式) {
// 循环体
}
三个部分,用分号隔开。我习惯这么理解:
- 初始化表达式:循环开始前执行一次,通常用来定义循环变量
- 循环条件:每次循环开始前判断,为真就继续,为假就退出
- 更新表达式:每次循环体执行完后执行,通常用来改变循环变量
举个最简单的例子:
for (int i = 0; i < 5; i++) {
printf("第%d次循环\n", i + 1);
}
这个代码会输出5行。i从0开始,每次加1,直到i等于5时条件不成立,循环结束。
我的习惯:在嵌入式开发中,我习惯把循环变量声明为int类型。虽然unsigned在某些场景下更安全,但int在边界判断时更直观,不容易出bug。
10.2 执行流程详解
for循环的执行顺序,我画了张图,你一看就明白:
流程其实很简单:
- 先执行初始化(只做一次)
- 判断条件是否成立
- 成立就执行循环体
- 执行更新表达式
- 回到第2步,继续判断
条件不成立时,直接跳到循环外面。
关键点:初始化只执行一次,条件判断每次循环前都做,更新表达式每次循环体结束后执行。
10.3 循环嵌套
循环嵌套,说白了就是循环里面套循环。我在做矩阵运算、图像处理时经常用到。
看个经典例子——打印九九乘法表:
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%d*%d=%2d ", j, i, i * j);
}
printf("\n");
}
输出结果:
1*1= 1
1*2= 2 2*2= 4
1*3= 3 2*3= 6 3*3= 9
1*4= 4 2*4= 8 3*4=12 4*4=16
...
外层循环控制行数,内层循环控制每行的列数。内层循环的结束条件依赖外层循环的变量i,这样每行的列数就不同了。
我曾经踩过的坑:有一次做二维数组遍历,我把内外层循环的变量名都写成了i。结果内层循环把外层i改了,程序跑飞了。从那以后,我坚持用i、j、k这种命名习惯,内外层绝不重复。
嵌套循环的时间复杂度是O(n²),数据量大时要小心。我在嵌入式项目里,如果循环层数超过3层,就会考虑优化算法。
10.4 死循环
死循环就是条件永远为真的循环。有时候是bug,有时候是故意为之。
先看几种常见的死循环写法:
// 写法1:条件永远为真
for (;;) {
// 循环体
}
// 写法2:条件写死为1
for (int i = 0; 1; i++) {
// 循环体
}
// 写法3:更新表达式没改变条件
for (int i = 0; i < 10; ) {
// 忘了写 i++
// 循环体
}
写法1在嵌入式开发中很常见。比如单片机的主循环:
for (;;) {
// 读取传感器数据
// 处理数据
// 控制输出
// 延时
}
这种死循环是故意的,因为嵌入式设备需要一直运行。
避坑指南:我曾经在调试一个电机控制程序时,不小心把循环条件写成了 i <= 10 而不是 i < 10。结果数组越界访问,电机突然反转,差点把设备搞坏。边界条件一定要仔细检查。
常见的死循环bug:
- 忘记写更新表达式(i++)
- 条件写反了(比如 i > 0 但 i 一直在增加)
- 浮点数比较(浮点数精度问题导致条件永远不成立)
- 循环体内修改了循环变量
10.5 for循环的变体用法
for循环的三个表达式都可以省略,但分号不能少。我分享几个实用技巧:
技巧1:多个变量同时控制
for (int i = 0, j = 10; i < j; i++, j--) {
printf("i=%d, j=%d\n", i, j);
}
技巧2:在循环体内改变循环变量
for (int i = 0; i < 100; i++) {
if (some_condition) {
i += 5; // 跳过一些元素
}
}
这种做法要谨慎,容易造成逻辑混乱。我一般只在特殊场景下用。
技巧3:使用break提前退出
for (int i = 0; i < 100; i++) {
if (data[i] == target) {
printf("找到了,位置在%d\n", i);
break;
}
}
我的建议:在嵌入式开发中,for循环的循环变量尽量用局部变量,不要用全局变量。全局变量容易被中断修改,导致循环行为不可预测。我吃过这个亏,调试了整整两天才找到原因。
10.6 性能小贴士
嵌入式设备资源有限,循环性能很重要。分享几个经验:
- 减少循环内的函数调用:把不变的表达式提到循环外面
- 用局部变量缓存数组长度:避免每次循环都计算
- 循环展开:对于固定次数的短循环,手动展开能提升速度
举个例子:
// 不推荐
for (int i = 0; i < strlen(str); i++) {
// 每次循环都计算strlen
}
// 推荐
int len = strlen(str);
for (int i = 0; i < len; i++) {
// 只计算一次
}
嗯,for循环的内容就这些。记住一点:循环是工具,用好了事半功倍。多写多练,自然就熟练了。
公众号:蓝海资料掘金营,微信deep3321