章节目录

for语句

本节阅读量:

C++中使用最多的循环语句是for语句。当有一个明显的循环变量时,for语句(也称为for循环)是首选,因为它允许我们轻松而简洁地定义、初始化、判断和更改循环变量的值。

从C++11开始,有两种不同的for循环。在本节中将介绍经典的for语句,在以后的课程中,介绍完一些其他先决主题(如数组和迭代器)后,将介绍新的range-for语句。

for语句在定义上看起来相当简单:

1
2
for (初始化语句; 条件表达式; 递增/递减表达式)
   语句;

可以将for循环,理解为等价的while语句:

1
2
3
4
5
6
7
8
{ // 整体都在代码块内
    初始化语句; // 用来定义循环变量
    while (条件表达式)
    {
        语句; 
        递增/递减表达式; // 用来改变循环变量,以便再次判断是否满足循环条件
    }
} // for循环中定义的所有变量,在这里都会被销毁

for语句的计算

for语句分为3阶段执行:

首先,执行初始化语句。这仅在循环启动时执行一次。初始化语句通常用于变量定义和初始化。定义的变量具有“循环作用域”,这实际上只是块作用域的一种形式,这些变量从定义点一直存在到循环语句的末尾。在等价的while循环中,可以看到初始化语句位于等价的代码块内,当整体结束时,初始化语句中定义的变量超出作用域。

其次,对于每次循环迭代,先计算条件表达式的结果。如果结果为true,则执行对应的语句。如果为false,则循环终止,并执行循环之外的下一条语句。

最后,计算递增/递减表达式。通常,该表达式用于递增或递减初始化语句中定义的循环变量。然后,会返回到第二步(并再次判断条件表达式)。

来看一个示例:

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

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

    std::cout << '\n';

    return 0;
}

首先,声明一个名为i的循环变量,并用值1对其进行初始化。

其次,计算i<=10,因为i是1,所以它的计算结果为true。因此,执行语句,输出1和空格。

最后,计算++i,将i增加到2。然后循环返回到第二步。

现在,再次计算i<=10。由于i的值为2,因此它的计算结果为true,因此循环再次迭代。打印2和一个空格,i递增为3。循环继续迭代,直到最终i增加到11,此时i<=10的计算结果为false,循环退出。

因此,该程序打印结果:

1
1 2 3 4 5 6 7 8 9 10

可以将上面的For循环转换为等效的while循环:

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

int main()
{
    { // 这个代码块,确定了变量 i 的作用域
        int i{ 1 }; // 初始化语句
        while (i <= 10) // 条件表达式
        {
            std::cout << i << ' '; // 循环中执行的语句
            ++i; // 递增/递减表达式
        }
    }

    std::cout << '\n';
}

注意,这里需要外部大括号,因为当循环结束时,变量 i 超出了作用域。

对于新程序员来说,循环可能很难阅读——然而,有经验的程序员喜欢它。这是使用计数器进行循环的一种非常紧凑的方法。并且在语句开头提供了关于循环变量、循环条件和循环变量变动的所有必要信息。这有助于减少错误。


更多for循环示例

下面是使用for循环计算整数的指数示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <cstdint> // 引入定宽整数

// 返回 base ^ exponent -- 注意会溢出!
std::int64_t pow(int base, int exponent)
{
    std::int64_t total{ 1 };

    for (int i{ 0 }; i < exponent; ++i)
        total *= base;

    return total;
}

此函数返回值为base^index(base的index次方)。

这是一个简单的for循环,其中i从0循环到(但不包括)exponent。

如果exponent为0,for循环将执行0次,函数将返回1。如果exponent为1,for循环将执行1次,函数将返回1base。如果exponent为2,for循环将执行2次,函数将返回1base*base。

尽管大多数for循环将循环变量增加1,但也可以将其减少:

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

int main()
{
    for (int i{ 9 }; i >= 0; --i)
        std::cout << i << ' ';

    std::cout << '\n';

    return 0;
}

结果:

1
9 8 7 6 5 4 3 2 1 0

或者,可以在每次迭代中将循环变量的值更改1以上:

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

int main()
{
    for (int i{ 0 }; i <= 10; i += 2) // 每次迭代 i 增加 2
        std::cout << i << ' ';

    std::cout << '\n';

    return 0;
}

结果:

1
0 2 4 6 8 10

for循环条件中,使用 != 操作符有些危险

在编写涉及值的循环条件时,通常可以用许多不同的方法编写条件表达式。以下两个循环执行结果相同:

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

int main()
{
    for (int i { 0 }; i < 10; ++i) // 使用 <
         std::cout << i;

    for (int i { 0 }; i != 10; ++i) // 使用 !=
         std::cout << i;

     return 0;
}

那么我们应该选择哪一个呢?前者是更好的选择,因为即使i跳过值10,它也会终止,而后者不会。下面的示例演示了这一点:

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

int main()
{
    for (int i { 0 }; i < 10; ++i) // 使用 <, 仍然会结束
    {
         std::cout << i;
         if (i == 9) ++i; // 条件表达式判断时会跳过10
    }

    for (int i { 0 }; i != 10; ++i) // 使用 !=, 无限循环
    {
         std::cout << i;
         if (i == 9) ++i; // 条件表达式判断时会跳过10
    }

     return 0;
}

少迭代一次的错误

对于for循环(以及使用计数器的其他循环),面临的最大问题之一是,条件表达式编写错误,循环多迭代或者少迭代一次。

下面是一个示例:

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

int main()
{
    // 这里用了 < 而不是 <=
    for (int i{ 1 }; i < 5; ++i)
    {
        std::cout << i << ' ';
    }

    std::cout << '\n';

    return 0;
}

本来期望打印12345,但它只打印1234,因为使用了错误的关系运算符。

这种错误的最常见原因是使用了错误的关系运算符,但有时也可能是因为递增递减操作写错导致。


省略的表达式

for语句中的任何部分都可省略。例如,在下面的示例中,将省略初始化语句和递增/递减表达式,只保留条件表达式:

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

int main()
{
    int i{ 0 };
    for ( ; i < 10; ) // 省略初始化语句和递增/递减表达式
    {
        std::cout << i << ' ';
        ++i;
    }

    std::cout << '\n';

    return 0;
}

此for循环产生以下结果:

1
0 1 2 3 4 5 6 7 8 9

这里不使用for循环来进行初始化和递增,而是手动完成。本例只是演示目的,但在某些情况下,需要不声明循环变量(因为已经有了一个)或不在递增/递减表达式做操作(因为其它地方已经操作过)。

值得注意的是,下面的示例生成了无限循环:

1
2
for (;;)
    语句;

上述示例相当于:

1
2
while (true)
    语句;

这可能有点出乎意料,因为您可能希望省略的条件表达式被视为false。然而,C++标准显式定义了for循环中省略的条件表达式应被视为true。

建议完全避免这种形式的for循环,而是使用while (true)。


对于具有多个计数器的循环

尽管for循环通常仅在一个变量上迭代,但有时for循环需要处理多个循环变量。为了实现这一点,可以在初始化语句中定义多个变量,并可以使用逗号操作符在递增/递减表达式中修改多个变量的值:

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

int main()
{
    for (int x{ 0 }, y{ 9 }; x < 10; ++x, --y)
        std::cout << x << ' ' << y << '\n';

    return 0;
}

该循环定义并初始化两个新变量:x和y。它在0到9的作用域内迭代x,每次迭代后x递增,y递减。

该程序生成结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
0 9
1 8
2 7
3 6
4 5
5 4
6 3
7 2
8 1
9 0

这是C++中唯一一个在同一语句中定义多个变量,并且使用逗号运算符被认为是可以接受的做法。


循环嵌套

与其他类型的循环一样,for循环可以嵌套在其他循环中。在下面的示例中,将for循环嵌套在另一个for循环中:

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

int main()
{
	for (char c{ 'a' }; c <= 'e'; ++c) // 遍历a到e的字母
	{
		std::cout << c; // 打印字母
		
		for (int i{ 0 }; i < 3; ++i) // 内层循环遍历0到2
			std::cout << i;

		std::cout << '\n';
	}

	return 0;
}

对于外部循环的每次迭代,内部循环都会完整地运行。因此,输出为:

1
2
3
4
5
a012
b012
c012
d012
e012

外部循环首先运行,char c被初始化为“a”。然后计算c<=“e”,为true,因此执行循环体。由于c被设置为“a”,因此首先打印a。然后,内部循环完全执行(它打印0、1和2)。然后打印换行符。现在,外部循环返回顶部,c增加到“b”,并重新计算循环条件。由于循环条件仍然为true,因此开始外部循环的下一次迭代。这将打印b012\n。等等。


总结

For语句是C++语言中最常用的循环。尽管它的语法对于新程序员来说通常有点混乱,但您会经常看到for循环,并很快就会理解它们!

如果有计数器,优先使用for语句,否则,可能while语句是更好的选择。


8.8 Do while语句

上一节

8.10 break与continue

下一节