重要的事情说三遍:
一个简单的C++语句是程序中的每个单独的指令,例如前面章节中看到的变量声明和表达式。它们总是以分号(;
)结束,并按它们在程序中出现的顺序执行。
但程序并不仅限于线性顺序的语句。在其过程中,程序可以重复代码段,或做出决策和分支。为此,C++提供了控制流语句,用于指定我们的程序在何时以及在何种情况下要做什么。
本节解释的许多控制流语句在其语法中需要一个通用(子)语句。这个语句可以是一个简单的C++语句,如一个以分号(;
)结束的单一指令,或者是一个复合语句。复合语句是一组语句(每个语句都以自己的分号结束),但都被聚集在一个用大括号括起来的块中:
{ statement1; statement2; statement3; }
整个块被视为一个单一语句(它本身由多个子语句组成)。每当控制流语句的语法中包含一个通用语句时,这可以是一个简单语句或复合语句。
选择语句:if 和 else
if
关键字用于执行一个语句或块,仅当一个条件满足时。其语法是:
if (condition) statement
这里,condition
是被评估的表达式。如果这个 condition
为真,则执行 statement
。如果为假,则不执行 statement
(它被简单地忽略),程序在整个选择语句之后继续执行。例如,以下代码片段仅在存储在 x
变量中的值确实为 100 时打印消息 "(x is 100)"
:
if (x == 100)
cout << "x is 100";
如果 x
不是 100,这个语句将被忽略,什么也不会打印。
如果你想在条件满足时执行多个语句,这些语句应括在大括号({}
)中,形成一个块:
if (x == 100)
{
cout << "x is ";
cout << x;
}
如往常一样,代码中的缩进和换行没有影响,因此上述代码等价于:
if (x == 100) { cout << "x is "; cout << x; }
通过使用 else
关键字,可以在条件不满足时指定要发生的事情,从而引入替代语句。其语法是:
if (condition) statement1 else statement2
其中 statement1
在条件为真时执行,而在条件不满足时执行 statement2
。例如:
if (x == 100)
cout << "x is 100";
else
cout << "x is not 100";
如果 x
的值确实是 100,这将打印 x is 100
,但如果不是,并且仅当不是时,它将打印 x is not 100
。
可以连接多个 if + else 结构以检查一系列值。例如:
if (x > 0)
cout << "x is positive";
else if (x < 0)
cout << "x is negative";
else
cout << "x is 0";
这通过连接两个 if-else 结构打印 x
是正数、负数或零。同样,也可以通过将它们分组在大括号 {}
中,在每种情况下执行多个语句。
迭代语句(循环)
循环重复一个语句若干次,或在条件满足时重复。它们由关键字 while
、do
和 for
引入。
while 循环
最简单的循环是 while 循环。其语法是:
while (expression) statement
while 循环只要 expression
为真就重复 statement
。如果在任何执行 statement
后 expression
不再为真,循环结束,程序在循环后继续执行。例如,让我们看一个使用 while 循环的倒计时:
// 自定义倒计时使用 while
#include <iostream>
using namespace std;
int main ()
{
int n = 10;
while (n>0) {
cout << n << ", ";
--n;
}
cout << "liftoff!\n";
}
main
中的第一个语句将 n
设置为 10。这是倒计时中的第一个数字。然后开始 while 循环:如果这个值满足条件 n>0
(即 n
大于零),则执行跟在条件之后的块,并在条件(n>0
)仍为真时重复执行该块。
上一个程序的整个过程可以根据以下脚本进行解释(从 main
开始):
n
被赋值- 检查
while
条件(n>0
)。此时有两种可能:- 条件为真:执行语句(转到第 3 步)
- 条件为假:忽略语句并继续执行后面的内容(转到第 5 步)
- 执行语句:
cout << n << ", "; --n;
(打印n
的值并将n
减 1) - 块结束。自动返回第 2 步。
- 块后继续程序:打印
liftoff!
并结束程序。
需要考虑的一点是 while 循环应在某个时刻结束,因此语句应以某种方式改变条件中检查的值,以便在某个时刻使其变为假。否则,循环将永远继续循环。在这种情况下,循环包括 --n
,它将被评估的变量(n
)的值减少 1,这最终将使条件(n>0
)在一定次数的循环迭代后变为假。具体来说,在 10 次迭代后,n
变为 0,使条件不再为真,并结束 while 循环。
请注意,对于计算机来说,这个循环的复杂性是微不足道的,因此整个倒计时立即完成,没有实际的延迟(如果有兴趣,请参见 sleep_for
以了解带有延迟的倒计时示例)。
do-while 循环
一个非常类似的循环是 do-while 循环,其语法是:
do statement while (condition);
它的行为类似于 while 循环,除了 condition
在执行 statement
之后而不是之前进行评估,保证至少执行一次 statement
,即使 condition
从未满足。例如,以下示例程序回显用户输入的任何文本,直到用户输入 goodbye:
// 回显机器
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str;
do {
cout << "Enter text: ";
getline (cin,str);
cout << "You entered: " << str << '\n';
} while (str != "goodbye");
}
当 statement
需要至少执行一次时,通常首选 do-while 循环,例如结束循环的条件在循环语句本身内确定的情况。在上一个示例中,块内的用户输入将决定循环是否结束。因此,即使用户希望尽快通过输入 goodbye 来结束循环,循环中的块也需要至少执行一次以提示输入,并且条件实际上只能在执行后确定。
for 循环
for
循环设计为迭代若干次。其语法是:
for (initialization; condition; increase) statement;
像 while 循环一样,这个循环在 condition
为真时重复 statement
。但此外,for 循环提供了特定的位置来包含 initialization
和 increase
表达式,分别在循环开始时和每次迭代后执行。因此,使用计数器变量作为 condition
特别有用。
它按以下方式工作:
- 执行
initialization
。通常,这声明一个计数器变量,并将其设置为某个初始值。这在循环开始时执行一次。 - 检查
condition
。如果为真,循环继续;否则,循环结束,跳过statement
,直接进入第 5 步。 - 执行
statement
。通常,它可以是一个单一语句或一个用大括号{ }
括起来的块。 - 执行
increase
,循环返回第 2 步。 - 循环结束:继续执行下一个语句。
这是使用 for 循环的倒计时示例:
// 使用 for 循环的倒计时
#include <iostream>
using namespace std;
int main ()
{
for (int n=10; n>0; n--) {
cout << n << ", ";
}
cout << "liftoff!\n";
}
for 循环中的三个字段是可选的。
它们可以留空,但在所有情况下,它们之间的分号是必需的。例如,for (;n<10;)
是没有 initialization
或 increase
的循环(相当于 while 循环);for (;n<10;++n)
是带有 increase
的循环,但没有 initialization
(也许是因为变量在循环之前已经初始化)。没有 condition
的循环相当于条件为 true
的循环(即无限循环)。
由于每个字段在循环生命周期的特定时间执行,在作为 initialization
、condition
或 statement
执行多个表达式可能很有用。不幸的是,这些不是语句,而是简单的表达式,因此不能用块替换。作为表达式,它们可以使用逗号运算符(,
):这个运算符是一个表达式分隔符,可以分隔通常只期望一个的多个表达式。例如,使用它,可以使 for 循环处理两个计数器变量,初始化并增加这两个变量:
for ( n=0, i=100 ; n!=i ; ++n, --i )
{
// 在此执行的内容...
}
如果在循环中既不修改 n
也不修改 i
,这个循环将执行 50 次:
n
以 0 值开始,i
以 100 值开始,条件是 n!=i
(即 n
不等于 i
)。因为每次迭代 n
增加 1,i
减少 1,循环的条件将在第 50 次迭代后变为假,此时 n
和 i
均为 50。
基于范围的 for 循环
for 循环还有另一种语法,仅用于范围:
for ( declaration : range ) statement;
这种 for 循环遍历 range
中的所有元素,其中 declaration
声明了一个能够获取该范围中元素值的变量。范围是元素的序列,包括数组、容器和任何支持 begin
和 end
函数的类型;这些类型大多数尚未在本教程中介绍,但我们已经熟悉至少一种范围:字符串,它们是字符序列。
使用字符串的基于范围的 for 循环示例:
// 基于范围的 for 循环
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str {"Hello!"};
for (char c : str)
{
cout << "[" << c << "]";
}
cout << '\n';
}
注意在 for 循环中冒号(:
)之前的是 char
变量的声明(字符串中的元素类型是 char
)。然后我们在语句块中使用这个变量 c
来表示范围中每个元素的值。
这个循环是自动的,不需要显式声明任何计数器变量。
基于范围的循环通常还使用 auto
进行元素类型的类型推导。通常,上述基于范围的循环也可以写成:
for (auto c : str)
cout << "[" << c << "]";
在这里,c
的类型被自动推导为 str
中元素的类型。
跳转语句
跳转语句允许通过执行跳转到特定位置来改变程序的流程。
break 语句
break
退出循环,即使条件未满足。它可以用于结束一个无限循环,或强制它在自然结束之前结束。例如,让我们在倒计时的自然结束之前停止它:
// break 循环示例
#include <iostream>
using namespace std;
int main ()
{
for (int n=10; n>0; n--)
{
cout << n << ", ";
if (n==3)
{
cout << "countdown aborted!";
break;
}
}
}
continue 语句
continue
语句使程序跳过当前迭代中的循环其余部分,好像已到达语句块的末尾,导致它跳到下一次迭代的开始。例如,让我们在倒计时中跳过数字 5:
// continue 循环示例
#include <iostream>
using namespace std;
int main ()
{
for (int n=10; n>0; n--) {
if (n==5) continue;
cout << n << ", ";
}
cout << "liftoff!\n";
}
goto 语句
goto
允许进行绝对跳转到程序的另一个点。此无条件跳转忽略嵌套级别,并且不会导致任何自动栈展开。因此,使用时需小心,并且最好在同一个语句块内使用,特别是在存在局部变量的情况下。
目标点由一个标签标识,然后用作 goto
语句的参数。标签由一个有效的标识符后跟一个冒号(:
)组成。
goto
通常被认为是一种低级功能,在使用 C++ 的现代高级编程范式中没有特定的用例。但是,仅作为一个示例,下面是使用 goto 的倒计时循环版本:
// goto 循环示例
#include <iostream>
using namespace std;
int main ()
{
int n=10;
mylabel:
cout << n << ", ";
n--;
if (n>0) goto mylabel;
cout << "liftoff!\n";
}
另一种选择语句:switch
switch 语句的语法有点特别。它的目的是在多个可能的常量表达式中检查一个值。它类似于连接 if
-else
语句,但限于常量表达式。其最典型的语法是:
switch (expression)
{
case constant1:
group-of-statements-1;
break;
case constant2:
group-of-statements-2;
break;
default:
default-group-of-statements
}
它按以下方式工作:switch
评估 expression
并检查它是否等于 constant1
;如果是,则执行 group-of-statements-1
,直到找到 break
语句。当找到这个 break
语句时,程序跳转到整个 switch
语句的末尾(结束大括号)。
如果表达式不等于 constant1
,则检查它是否等于 constant2
。如果等于此,则执行 group-of-statements-2
,直到找到 break
,然后跳转到 switch 的末尾。
最后,如果表达式的值不符合之前指定的任何常量(这些常量可以有任意数量),程序执行包含在 default:
标签之后的语句(如果存在,因为它是可选的)。
以下两个代码片段具有相同的行为,演示了 switch 语句的 if-else 等效形式:
// 使用 if-else 语句
if (x == 1) {
cout << "x is 1";
} else if (x == 2) {
cout << "x is 2";
} else if (x == 3) {
cout << "x is 3";
} else {
cout << "x is not 1, 2, or 3";
}
// 使用 switch 语句
switch (x) {
case 1:
cout << "x is 1";
break;
case 2:
cout << "x is 2";
break;
case 3:
cout << "x is 3";
break;
default:
cout << "x is not 1, 2, or 3";
}
switch 语句的语法继承自早期的 C 编译器,因为它使用标签而不是块。在最典型的用法中(如上所示),这意味着在每个标签的语句组之后需要 break
语句。如果不包括 break
,则在标签之后的所有语句(包括其他标签下的语句)也会执行,直到到达 switch 块的末尾或跳转语句(如 break
)为止。
如果上面的示例在第一个标签组之后没有 break
语句,程序在打印 x is 1
后不会自动跳转到 switch 块的末尾,而是继续执行第二个标签中的语句(因此也会打印 x is 2
)。它将继续这样做,直到遇到 break
语句或到达 switch
块的末尾。这使得不需要将每个标签的语句括在大括号 {}
中,并且在不同的可能值执行相同的语句组时非常有用。例如:
switch (x) {
case 1:
case 2:
case 3:
cout << "x is 1, 2 or 3";
break;
default:
cout << "x is not 1, 2 nor 3";
}
注意,switch 限于将其评估表达式与常量表达式进行比较。不能使用变量作为标签或范围,因为它们不是有效的 C++ 常量表达式。
要检查范围或非常量值,最好使用连接的 if
和 else if
语句。