章节目录

循环和while语句简介

本节阅读量:

循环简介

现在真正有趣的部分开始了——在接下来的课程中,我们将讨论循环。循环允许一段代码重复执行,直到满足某个退出条件。循环为编程带来了极大的灵活性。

例如,假设您希望打印1到10之间的所有数字。如果没有循环,您可能尝试如下操作:

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    std::cout << "1 2 3 4 5 6 7 8 9 10";
    std::cout << " done!\n";
    return 0;
}

虽然这是可行的,但随着要打印的数字变多,这种做法会越来越困难:如果您想打印1到1000之间的所有数字,该怎么办?那将需要相当多的代码!不过,这样的程序仍然可以硬写出来,因为要打印多少个数字在编译时就已经确定。

现在,让我们稍微改变一下需求。如果我们想让用户输入一个数字,然后打印1到该数字之间的所有数字,该怎么办?用户会输入什么数字在编译时并不知道。该如何解决这个问题呢?


While语句

while语句(也称为while循环)是C++提供的三种循环类型中最简单的一种,它的定义与if语句的定义非常相似:

1
2
while (条件表达式)
    语句;

使用while关键字声明while语句。执行while语句时,将计算条件表达式。如果条件的计算结果为true,则执行关联的语句。

然而,与if语句不同,一旦关联语句执行完成,控制权就会返回while语句的顶部,并重复这一过程。这意味着只要条件继续计算为true,while语句就会一直循环。

让我们来看一个简单的while循环,它打印从1到10的所有数字:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>

int main()
{
    int count{ 1 };
    while (count <= 10)
    {
        std::cout << count << ' ';
        ++count;
    }

    std::cout << "done!\n";

    return 0;
}

输出:

1
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,与循环关联的语句会被跳过。至此,循环结束。

虽然这个程序比直接键入1到10之间的所有数字稍微多一些代码,但请想一想,把它改成打印1到1000之间的所有数字有多容易:只需要将count <= 10改为count <= 1000。


初始条件计算为false的语句

请注意,如果条件最初的计算结果为false,则关联的语句将根本不会执行。考虑以下程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#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循环将永远执行。这称为死循环。下面是示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>

int main()
{
    int count{ 1 };
    while (count <= 10) // 这个条件表达式的结果永远为true
    {
        std::cout << count << ' '; // 这一行将无限的重复执行
    }

    std::cout << '\n'; // 这一行不会执行到

    return 0; // 这一行也不会执行到
}

由于count在此程序中从不递增,因此count <= 10将始终为true。因此,循环永远不会终止,程序会一直打印1。


有意的死循环

可以这样声明有意的死循环:

1
2
3
4
while (true)
{
  // 这个循环将无限执行
}

退出无限循环的唯一方法是通过return语句、break语句、exit语句、goto语句、引发异常或用户终止程序。

下面用一个简单的例子来演示这一点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#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。此时if语句的条件为true,并返回0;这会导致main()函数退出,从而终止程序。

在连续运行并为web请求提供服务的web服务器应用程序中,经常会看到这种循环。


循环变量的命名

循环变量是用于控制循环执行次数的变量。例如,给定while ( count <= 10 ),count是一个循环变量。虽然大多数循环变量的类型都是int,但您偶尔会看到其他类型(例如char)。

循环变量通常被赋予简单的名称,其中i、j和k是最常见的。

然而,如果您想知道程序中哪里使用了某个循环变量,并搜索i、j或k,搜索结果可能会覆盖程序中的大量行!因此,一些开发人员更喜欢使用更独特的循环变量名,例如iii、jjj或kkk。由于这些名称更少见,搜索循环变量会更容易,也能让它们作为循环变量更明显。更好的做法是使用“真实”的变量名,例如count、index,或者能说明计数内容的名称(例如userCount)。

最常见的循环变量类型称为计数器,它是一个循环变量,用于统计循环已执行的次数。在上面的示例中,变量count是一个计数器。


整型循环变量应该有符号

整型循环变量几乎总是有符号的,因为无符号整数可能会导致意外问题。考虑以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

int main()
{
    unsigned int count{ 10 }; // note: 无符号整数

    // 从10数到0
    while (count >= 0)
    {
        if (count == 0)
        {
            std::cout << "blastoff!";
        }
        else
        {
            std::cout << count << ' ';
        }
        --count;
    }

    std::cout << '\n';

    return 0;
}

看看上面的例子,试着找出其中的错误。如果你以前没有见过这种问题,它并不明显。

事实证明,这个程序是一个无限循环。它一开始会按预期打印“10 9 8 7 6 5 4 3 2 1 blastoff!”,但随后循环变量count发生溢出,并从4294967295开始倒计时(假设它是32位整数)。为什么?因为循环条件count >= 0永远不会为false!当count为0时,0 >= 0为true。然后执行–count,count回绕到4294967295。由于4294967295 >= 0仍为true,程序会继续执行。因为count是无符号的,所以它永远不可能为负;也正因为如此,循环不会终止。


每N次迭代执行一次操作

每次执行循环时,它被称为一次迭代。

通常,我们希望每隔2、3或4次迭代执行某些操作,例如打印换行。通过对计数器使用余数运算符,可以轻松完成此操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>

// 遍历1到50中的每一个数字
int main()
{
    int count{ 1 };
    while (count <= 50)
    {
        // 打印数字 (如果小于10,打印一个前导0,对齐输出)
        if (count < 10)
        {
            std::cout << '0';
        }

        std::cout << count << ' ';

        // 如果循环变量是10个整数倍, 打印换行
        if (count % 10 == 0)
        {
            std::cout << '\n';
        }
            
        // 递增循环变量
        ++count;
    }

    return 0;
}

该程序生成结果:

1
2
3
4
5
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

嵌套循环

还可以将循环嵌套在其他循环的内部。在下面的示例中,嵌套循环(我们称之为内部循环)和外部循环都有自己的计数器。注意,内部循环的循环表达式也使用了外部循环的计数器!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

int main()
{
    // 外层循环遍历1到5
    int outer{ 1 };
    while (outer <= 5)
    {
        // 每个外层循环的变量, 都执行一次内层循环的代码

        // 内层循环遍历1到outer
        int inner{ 1 };
        while (inner <= outer)
        {
            std::cout << inner << ' ';
            ++inner;
        }

        // 打印换行
        std::cout << '\n';
        ++outer;
    }

    return 0;
}

该程序打印:

1
2
3
4
5
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5

嵌套循环对新程序员来说往往不容易理解。如果您觉得有点困惑,请不要气馁。外部循环每迭代一次,外部循环体就执行一次。由于外部循环体包含内部循环,因此外部循环每迭代一次,内部循环也会执行。

让我们更详细地研究一下这是如何工作的。

首先,我们有一个外循环(具有循环变量outer),它将循环5次(outer依次具有值1、2、3、4和5)。

在外部循环的第一次迭代中,outer的值为1,然后执行外部循环体。在外部循环体内部,还有另一个包含循环变量inner的循环。内部循环从1迭代到outer(值为1),因此该内部循环会执行一次,打印值1。然后打印一个换行,并将outer增加到2。

在外部循环的第二次迭代中,outer的值为2,然后执行外部循环体。在外部循环体内部,inner从1迭代到outer(此时值为2),因此该内部循环会执行两次,打印值1和2。然后打印一个换行,并将outer增加到3。

该过程会继续下去,在后续迭代中,内部循环会打印1 2 3、1 2 3 4和1 2 3 4 5。最终,outer增加到6;由于外部循环条件(outer <= 5)为false,外部循环结束。随后程序结束。

如果您仍然感到困惑,请在调试器中逐行遍历该程序,并观察inner和outer的值,这是更好地理解正在发生的事情的好方法。


8.6 goto语句

上一节

8.8 Do while语句

下一节