7.7 - 循环和 while 语句
Key Takeaway
循环
现在真正的乐趣开始了——在接下来的课程中,我们将介绍循环。循环是控制流结构,它允许一段代码被重复执行,直到满足某些条件。循环为编程工具包增加了很大的灵活性,帮助我们完成很多不使用循环难以实现的功能。
例如,假设你想要打印1到10,如果没有循环,你可能会这么做:
1 2 3 4 5 6 7 8 |
|
虽然这么做是可行的,但当我们需要打印更多的数字时,它会变得越来越不可行:如果需要打印1到1000之间的所有数字呢?那打字的工作量就太大了!而且该程序要打印的内容必须在编译时就知道要打印多少数字。
现在,让我们稍微改变一下参数。如果需要让用户输入一个数字,然后打印1和用户输入的数字之间的所有数字,那会怎么样呢?用户将输入的数字在编译时是不可知的,要怎么解决这个问题呢?
while
语句
while
语句(也被叫做while
循环)是C++中最简单循环,它的定义和 if 语句特别地像:
1 2 |
|
while
语句的声明需要使用while
关键字。当 while
语句执行时,condition
会被求值。如果求值结果是真,则相关的语句会被执行。
但是,和if语句不同的是,while
语句中的statement
执行完成后,会返回到while
语句的开始,然后继续执行、这意味着只要条件求值为真,while
循环就会不停地执行。
还是打印1到10的例子,这次使用while
循环完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
输出:
1 |
|
让我们仔细看看这个程序正在做什么。首先,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
的while
语句
如果初始化条件为false
,那么语句就完全不会被执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
由于 15 <= 10
求值为 false
,所以while
中的所有语句都会被跳过,程序继续执行并打印done
。
无限循环
如果表达式始终求值为true
,则while
循环就会一直运行——称为无限循环或死循环。请看下面的例子中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
因为 count
始终没有被递增,所以 count <= 10
总是真。因此循环永远不会停止,程序不停地打印 “1 1 1 1 1″。
有意而为之的无限循环
我们可以故意创建一个无限循环:
1 2 3 4 |
|
退出无限循环的唯一方法是使用 return
y 语句、break
语句、exit
语句、goto
语句、抛出异常或被其他程序终止。
请看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
该循环会不断执行,直到用户输入n
,此时if语句的条件求值为真,return 0
被执行,main函数退出,程序终止。
在持续运行并为web请求提供服务的web服务器应用程序中,经常可以看到这种循环。
最佳实践
对于有意而为之的死循环,请使用 while(true)
。
循环变量
通常,我们会希望一个循环执行一定次数。此时可以使用循环变量,称为计数器。循环变量是一个整数,用于计算循环执行了多少次。在上面的例子中,变量 count
是一个循环变量。
循环变量通常有简单的名称,如i 、 j 或 k 。但是,如果你想知道循环变量在程序中的哪个位置被使用,此时你会搜索 i 、j 或 k ,此时会返回大量的搜索结果!因此,一些开发人员喜欢用 iii
、jjj
或 kkk
这样的变量名。因为这些名称更加独特,这使得搜索循环变量更加容易,并帮助它们作为循环变量脱颖而出。一个更好的主意是使用“真实的”变量名,比如count
,或者一个关于你正在计数的东西的更详细的名字(例如userCount
)。
循环变量应该为有符号数
循环变量几乎都应该是有符号的,因为无符号整数可能导致意想不到的问题。考虑以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
看一下上面的例子,看看能否发现错误。并不是很明显。
事实证明,这个程序是一个无限循环。它开始打印 10 9 8 7 6 5 4 3 2 1 blastoff
,但随后就脱轨了,从4294967295
开始倒数。为什么?因为循环条件 count >= 0
永远不会为假!当count
为 0 时,0 >= 0
为真。然后执行--count
, count
反转为4294967295
(假设是32位整数)。由于 4294967295 >= 0
为真,程序继续执行。因为 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 |
|
打印:
1 2 3 4 5 |
|
嵌套循环
循环可以嵌套在其他循环中。在下面的例子中,被嵌套循环(我们称之为内层循环)和外层循环都有各自的计数器。注意,内部循环的循环表达式也使用了外部循环的计数器!
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 |
|
程序打印:
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”的值,通过这个方法你可以更好地理解代码的运行逻辑。