循环与 while 语句简介

循环简介

真正的乐趣现在开始——在接下来的课程中,我们将学习循环。循环是一种控制流结构,它允许某段代码在满足某一条件之前反复执行。循环极大地增强了编程的灵活性,使我们能够轻松完成许多原本困难的任务。

例如,假设需要打印 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!

程序解析:

  1. 定义整型变量 count 并初始化为 1。
  2. 条件 count <= 10 为 true,执行循环体。循环体是一个语句块,首先打印 1 和一个空格,然后将 count 增加到 2。
  3. 控制权回到 while 顶端,再次判断条件。2 <= 10 为 true,继续执行。
  4. 当 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 循环。

执行过程:

  1. outer 从 1 到 5 循环。
  2. 每次 outer 迭代时,inner 从 1 循环到 outer 并打印。
  3. 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

(解答部分略,保留原英文链接)

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"