循环简介
真正的乐趣现在开始——在接下来的课程中,我们将学习循环。循环是一种控制流结构,它允许某段代码在满足某一条件之前反复执行。循环极大地增强了编程的灵活性,使我们能够轻松完成许多原本困难的任务。
例如,假设需要打印 1 到 10 的所有整数。不使用循环,你可能写成:
#include <iostream>
int main()
{
std::cout << "1 2 3 4 5 6 7 8 9 10";
std::cout << " done!\n";
return 0;
}
虽然可行,但若想打印 1 到 1000 的所有整数,手动输入显然不切实际。之所以仍能用上述方式编写,是因为我们在编译时就已知道要打印多少个数。
现在稍作变化:若程序运行时请用户输入一个整数 n,再打印 1 到 n 的所有整数,由于 n 在编译时未知,该如何实现?
while 语句
while 语句(又称 while 循环)是 C++ 提供的三种循环类型中最简单的一种,其定义与 if 语句非常相似:
while (condition)
statement;
while 语句以关键字 while 开头。执行时,首先求值 condition;若结果为 true,则执行对应的 statement。与 if 语句不同,statement 执行完毕后,控制权重新回到 while 语句顶端,并重复上述过程。因此,只要 condition 持续为 true,while 语句便会一直循环。
以下示例使用 while 循环打印 1 到 10:
#include <iostream>
int main()
{
int count{ 1 };
while (count <= 10)
{
std::cout << count << ' ';
++count;
}
std::cout << "done!\n";
return 0;
}
输出:
1 2 3 4 5 6 7 8 9 10 done!
程序解析:
- 定义整型变量 count 并初始化为 1。
- 条件 count <= 10 为 true,执行循环体。循环体是一个语句块,首先打印 1 和一个空格,然后将 count 增加到 2。
- 控制权回到 while 顶端,再次判断条件。2 <= 10 为 true,继续执行。
- 当 count 变为 11 时,11 <= 10 为 false,循环体被跳过,循环结束。
虽然代码量略多于直接打印,但只需将 count <= 10 改为 count <= 1000 即可打印 1 到 1000,十分方便。
条件初始即为 false 的 while 语句
若条件首次求值即为 false,则循环体一次也不执行。示例:
#include <iostream>
int main()
{
int count{ 15 };
while (count <= 10)
{
std::cout << count << ' ';
++count;
}
std::cout << "done!\n";
return 0;
}
由于 15 <= 10 为 false,循环体被跳过,仅打印 done!。
无限循环
若条件恒为 true,while 循环将永不终止,称为无限循环。示例:
#include <iostream>
int main()
{
int count{ 1 };
while (count <= 10) // 条件永不为 false
{
std::cout << count << ' '; // 该行将无限执行
}
std::cout << '\n'; // 该行永不执行
return 0; // 该行永不执行
}
因 count 未被递增,count <= 10 始终为 true,程序将不停打印 1 1 1 1…。
故意无限循环
可显式创建故意无限循环:
while (true)
{
// 永远执行
}
唯一退出方式:return、break、exit、goto、抛出异常或用户终止程序。示例:
#include <iostream>
int main()
{
while (true)
{
std::cout << "Loop again (y/n)? ";
char c{};
std::cin >> c;
if (c == 'n')
return 0;
}
return 0;
}
程序持续运行,直到用户输入 n,此时 return 0 终止 main 函数。此类循环常用于持续服务的 Web 服务器程序。
最佳实践:有意无限循环请使用 while(true)。
无意无限循环
在 while 条件后误加分号易导致程序挂起。示例:
#include <iostream>
int main()
{
int count{ 1 };
while (count <= 10); // 注意此处分号
{
std::cout << count << ' ';
++count;
}
std::cout << "done!\n";
return 0;
}
编译器实际将其理解为:
while (count <= 10)
; // 空语句,无限循环
{
... // 此块与 while 无关
}
因 count 未改变,循环永不终止。若有意让函数反复调用直至返回 false,可写作:
while (keepRunning());
若 keepRunning 永不返回 false,则形成无限循环。
警告:在 while 条件后加分号须谨慎,除非确知条件可变为 false。
循环变量及其命名
循环变量用于控制循环次数。例如,在 while (count <= 10) 中,count 即循环变量。多数循环变量为 int 型,偶尔也用 char 等。常见简单名称为 i、j、k。
背景:在 Fortran 中,i、j、k 是前三个最短整型变量名,该约定沿用至今。然若用搜索功能查找 i、j、k,结果可能遍布整个程序。因此部分开发者改用 iii、jjj、kkk,或更具语义的名称如 count、index、userCount 等。
最常见的循环变量为计数器(counter),用于统计已执行次数。上述示例中的 count 即为计数器。
整型循环变量应使用有符号类型
整型循环变量几乎总应为有符号类型,否则可能产生意外行为。示例:
#include <iostream>
int main()
{
unsigned int count{ 10 }; // 注意:无符号
while (count >= 0)
{
if (count == 0)
std::cout << "blastoff!";
else
std::cout << count << ' ';
--count;
}
std::cout << '\n';
return 0;
}
该程序为无限循环。打印 10 9…1 blastoff! 后,count 自减至 0,再减 1 时溢出为 4294967295(32 位整型),count >= 0 永远为真,循环无法终止。
最佳实践:整型循环变量一般应为有符号整型。
每 N 次迭代执行特定操作
每次循环执行称为一次迭代。常需在每 2、3、4… 次迭代时执行特殊操作,例如换行。利用计数器取模即可:
#include <iostream>
int main()
{
int count{ 1 };
while (count <= 50)
{
if (count < 10)
std::cout << '0';
std::cout << count << ' ';
if (count % 10 == 0)
std::cout << '\n';
++count;
}
return 0;
}
输出:
01 02 03 04 05 06 07 08 09 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
嵌套循环
循环可嵌套在其他循环内。初学者常觉困惑,先看简单示例:
#include <iostream>
void printUpto(int outer)
{
int inner{ 1 };
while (inner <= outer)
{
std::cout << inner << ' ';
++inner;
}
}
int main()
{
int outer{ 1 };
while (outer <= 5)
{
printUpto(outer);
std::cout << '\n';
++outer;
}
return 0;
}
输出:
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
此例中,外层循环的循环体调用 printUpto(),而 printUpto() 内部又含循环,故形成嵌套。
再看稍复杂示例:
#include <iostream>
int main()
{
int outer{ 1 };
while (outer <= 5)
{
int inner{ 1 };
while (inner <= outer)
{
std::cout << inner << ' ';
++inner;
}
std::cout << '\n';
++outer;
}
return 0;
}
输出同上。此处将 printUpto() 的代码直接放入外层循环体,形成显式嵌套 while 循环。
执行过程:
- outer 从 1 到 5 循环。
- 每次 outer 迭代时,inner 从 1 循环到 outer 并打印。
- outer 增至 6 时,外层条件为假,程序结束。
若仍有疑惑,可在调试器中逐行跟踪 inner 与 outer 的值。
测验
问题 1
在上述程序中,为何变量 inner 声明在 while 块内,而不是紧跟在 outer 之后?
问题 2
编写程序,打印 a 到 z 及其 ASCII 码,循环变量类型为 char。
问题 3
将嵌套循环示例反转,使其输出:
5 4 3 2 1
4 3 2 1
3 2 1
2 1
1
问题 4
修改程序,使数字以下列格式输出:
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
提示:先实现:
X X X X 1
X X X 2 1
X X 3 2 1
X 4 3 2 1
5 4 3 2 1
(解答部分略,保留原英文链接)