为什么需要 for 循环
switch 选出一个分支,执行一次。但真实的程序需要反复做某件事:打印每个分数、对一组数字求和、画出网格的 10 行。for 循环是 C++ 中把代码重复执行已知次数的主力工具,自带一个由你控制的计数器。
for 循环需要的一切都集中在一个紧凑的循环头里,所以“执行多少次、怎么执行”一眼就能看清。
三段式循环头
for 循环头有三个部分,用分号隔开:初始化、条件和更新。
for (initializer; condition; update) {
// body - runs while condition is true
}
它们按特定的顺序执行:初始化在开始时运行一次;然后在每次迭代之前检查条件;只有条件为 true 时才运行循环体;更新在每次迭代结束时运行,紧接着再次检查条件。
这里 int i = 0 只运行一次。接着检查 i < 5:只要它成立,循环体就打印一行,然后 i++ 让计数器加一。当 i 变成 5 时,条件为 false,于是循环退出,打印出 done。循环体恰好运行 5 次,i 依次取 0 到 4。
在循环头里声明计数器(int i = 0)能让 i 的作用域限定在循环之内:右花括号之后它就不存在了,这正是你想要的。
正向计数、反向计数与按步长计数
更新部分并不局限于 i++。你可以倒着数,可以按任意步长前进,也可以遍历数组下标。
第一个循环在 i > 0 时运行,每次减一,所以打印 5 4 3 2 1。第二个循环每轮加 2,并使用 <= 10,因为 10 是我们想包含的值。让条件和更新相互匹配:倒着数搭配 > 或 >=,正着数搭配 < 或 <=。
遍历数组
计数循环最常见的用途是按下标遍历数组。计数器同时也是你要读取的位置。
注意条件是 i < n,而不是 i <= n。一个有 5 个元素的数组,有效下标是 0 到 4;下标 5 已经越过末尾了。读取 scores[5] 是未定义行为:它可能打印出垃圾值、崩溃,或者看起来正常运行却悄悄破坏了内存。对于任何从零开始计数的数组,i < n 都是安全的默认写法。
如果你只需要值而不需要下标,基于范围的 for 更简洁。当你确实需要位置时,再使用经典的下标循环。
break 与 continue
有两个关键字能让你在循环中途改变流程。break 立即退出循环;continue 跳过当前迭代剩下的部分,直接跳到更新。
第一个循环一找到 7 就停止,不再检查剩下的数。第二个循环在 i 为偶数时用 continue 跳过循环体里的打印,但更新 i++ 依然会运行,所以循环会继续向前推进。一个不易察觉的坑:continue 跳到的是更新部分,所以如果某个循环的计数器不是在循环头里、而是在循环体内部更新的,你又依赖了 continue,就可能意外跳过那次更新,陷入死循环。
嵌套循环
把一个 for 放进另一个里面,就能处理网格、表格或成对的数据。外层循环每走一步,内层循环都会完整地运行一遍。
这会打印一个 3x3 的乘法表。外层循环固定一个 row,内层循环扫过这一行的每个 col,然后用换行结束这一行。给计数器起不同的名字(row/col,而不是 i/i):重复使用同一个名字会遮蔽外层的变量,产生让人摸不着头脑的 bug。还要注意开销:把一个 n 次循环嵌套在另一个 n 次循环里,循环体会运行 n * n 次,次数增长得很快。
常见的坑
C++ 中大多数 for 循环 bug 都出自下面几个陷阱:
- 差一错误:对从零开始计数的大小使用
i <= n,会多读一个越过末尾的元素。用i < n。 - 无符号数下溢:用无符号类型倒着数,永远不会变成负数。
for (size_t i = n - 1; i >= 0; i--)会永远循环下去,因为对于无符号值,i >= 0永远为真:当i是0时,i--会绕回成一个巨大的正数。倒着数时使用有符号的int,或者改写条件。 - 在循环体内修改计数器:既在循环头里又在循环体里修改
i,会让循环次数变得难以预测。只选一个地方修改。
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
process(arr[i]);
}
浮点数计数器是另一个隐蔽的风险:for (double x = 0.0; x != 1.0; x += 0.1) 可能永远无法恰好等于 1.0,因为 0.1 无法被精确存储。用整数计数,在循环内部计算这个值,或者用 < 代替 !=。
下一步:while 循环
当你事先知道次数时,for 循环最好用。但有时你需要一直重复,直到某个条件发生变化为止,比如一直读取输入直到文件末尾、一直重试直到成功,而步数并不固定。这正是 while 循环的工作,它把循环头精简到只剩一个条件。下一页就讲它。
常见问题
C++ 中怎么写 for 循环?
在循环头里写三个部分,用分号隔开:初始化、条件和更新。for (int i = 0; i < 5; i++) { cout << i; } 会让 i 依次取 0、1、2、3、4 来运行循环体。一旦条件变为 false,循环就停止。
C++ 中普通 for 循环和基于范围的 for 循环有什么区别?
经典的 for 给你一个由你控制的下标计数器(for (int i = 0; i < n; i++)),当你需要知道位置,或者想按自定义的方式步进时就要用它。基于范围的 for(for (int x : v))把下标隐藏起来,直接把每个元素交给你,如果你只需要值,这样写更简洁。
为什么我的 C++ for 循环多跑了一次或少跑了一次?
这就是经典的差一错误(off-by-one)。对从零开始计数的大小用 <= 而不是 <,会多跑一次迭代并越过数组末尾去读取;本想包含最后一个值却用了 <,就会少跑一次。对于大小为 n 的数组,安全的写法是 for (int i = 0; i < n; i++)。