没有计数器的时候
for 循环是围绕一个事先设好的计数器构建的。但很多循环并没有明确的次数:不断读取数字直到输入结束,不断要求输入密码直到正确,不断把一个值减半直到它变成 1。对于这些情况,while 循环是最自然的选择:只要条件保持为 true,它就一直重复。
while 循环在每次执行之前都会检查条件,包括第一次。如果条件一开始就是 false,循环体根本不会执行。
先检查条件 count > 0;如果为 true 就执行循环体,然后回到开头再次检查。count-- 这一行最终会让条件变为 false,去掉它,循环就会永远运行下去。
while 循环的结构
把它和你已经熟悉的三段式 for 循环比较一下。while 循环把这三件事拆开了:初始化在循环之前完成,条件写在括号里,更新则放在循环体内部。
int i = 0; // initialization - before the loop
while (i < 5) { // condition - checked each pass
cout << i << "\n";
i++; // update - you must remember this yourself
}
最后一点正是要注意的地方。在 for 循环中,更新部分就紧挨着条件,很难忘记。而在 while 循环中,它只是循环体里的又一条语句,最常见的 bug 就是漏写了它,导致程序卡死。
还有一个专坑初学者的 C++ 陷阱:条件后面多出的一个分号,会让循环体变成一条空语句。
int i = 0;
while (i < 5); // <-- this semicolon is the entire loop body
{
cout << i << "\n";
i++;
}
这会什么也不做地永远循环下去,因为 ; 就是循环体,而 i 永远不会改变。花括号里的代码块随后只会执行一次。编译器不会阻止你:这是完全合法的代码,只是不是你想要的。
do-while:至少执行一次循环体
有时你需要先执行一次循环体,才能决定是否要重复。do-while 循环在底部检查条件,所以循环体总是至少执行一次:
你必须至少提示并读取一次,才能知道输入是否有效,而这正是 do-while 提供的。注意 while (...) 后面的分号:在 C++ 中,do-while 必须写这个分号,忘了它是常见的编译错误。
当条件一开始就是 false 时,它和普通 while 的区别就一目了然了:
只有 do-while body 被打印出来。while 循环完全跳过了它的循环体,因为在第一次检查之前 x < 5 就已经是 false。
循环直到 cin 失败
while 的一个经典用法是一直读取,直到输入停止:没有计数器,只是一直读下去,直到输入流告诉你该结束了。在 C++ 中,cin >> value 的求值结果就是输入流本身,读取成功时它为真,一旦遇到输入结束或类型不匹配就为假。这让它成为一个简洁的循环条件:
这个条件身兼两职:一步之内既读取又检测。再对比一下哨兵值的写法,比如遇到 0 就停止。这时你必须在循环之前读取一次,并在每次循环的末尾再读取一次,让条件检测的始终是新输入:
如果忘了循环体内的第二次读取,value 就永远不会改变,你就得到了一个死循环。while (cin >> value) 这种写法把读取并入了条件,从而避开了这个问题。
while 循环中的 break 和 continue
break 和 continue 在这里的作用和在 for 循环中一样。break 立即离开循环;continue 直接跳回条件检查,跳过本次循环剩下的部分:
这会打印 1 3 5 7 9。while (true) 有意设计成自己永远不会停下来,break 是唯一的出口。在 while 循环中使用 continue 要小心:因为更新是循环体的一部分,在推进计数器之前就用 continue 跳回开头,是让循环悄悄卡死的一种方式。上面的例子中 n++ 先执行,所以是安全的。
当心死循环
条件最终必须变为 false,而这完全取决于循环体内有没有东西在改变。两个常见的元凶是:忘了更新,以及更新的方式恰好跳过了退出值:
int i = 0;
while (i < 5) {
cout << i << "\n"; // i never changes -> runs forever
}
int i = 0;
while (i != 10) {
i += 3; // 0, 3, 6, 9, 12... steps right past 10 -> runs forever
}
第一个卡住是因为 i 从未更新。第二个卡住是因为计数器跨过了条件要找的那个确切的值:当步长不一定能正好落在目标值上时,优先用 < 或 <= 而不是 !=。while (true) 本身没问题,前提是它有一个必定会执行到的 break;意外写出来的那种就只是 bug。还要记住前面讲过的空循环体 while (...); 陷阱:一个放错位置的分号,会产生一个乍看之下完全正确的死循环。
下一步:基于范围的 for 循环
当你需要一直循环到条件改变、又没有明确的次数时,while 循环是合适的工具。但如果你只是想访问容器(vector、数组、string)中的每一个元素,不需要管理下标,也没有条件要维护,现代 C++ 有更简洁的写法:基于范围的 for 循环(for (auto x : container))。这就是下一页的内容。
常见问题
C++ 中 while 和 do-while 有什么区别?
while 循环在第一次执行之前就检查条件,所以循环体可能一次都不执行。do-while 循环先执行一次循环体,再在底部检查条件,所以它至少会执行一次。当必须先做某件事才能决定是否重复时(比如先提示输入,再验证输入),就用 do-while。注意 do-while 在结尾的 while (...) 之后需要一个分号。
C++ 中什么时候该用 while 循环而不是 for 循环?
当你没有一个明确的计数器,只想一直重复到某个条件改变时,就用 while 循环:读取输入直到 cin 失败、轮询直到某个值就绪、处理队列直到它为空。当你知道循环次数,或者有一个明显可以计数的下标时,就用 for 循环。
C++ 中如何停止一个死循环的 while 循环?
确保循环体内有某个操作最终会让条件变为 false(让计数器递减、移动指针、设置一个标志)。对于有意写的 while (true) 循环,要在里面放一个由 if 控制的 break。如果循环卡住了,通常的原因是忘了更新条件所依赖的那个变量。