控制结构(Statements and flow control)

重要的事情说三遍:

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

一个简单的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 是正数、负数或零。同样,也可以通过将它们分组在大括号 {} 中,在每种情况下执行多个语句。

迭代语句(循环)

循环重复一个语句若干次,或在条件满足时重复。它们由关键字 whiledofor 引入。

while 循环

最简单的循环是 while 循环。其语法是:

while (expression) statement

while 循环只要 expression 为真就重复 statement。如果在任何执行 statementexpression 不再为真,循环结束,程序在循环后继续执行。例如,让我们看一个使用 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 开始):

  1. n 被赋值
  2. 检查 while 条件(n>0)。此时有两种可能:
    • 条件为真:执行语句(转到第 3 步)
    • 条件为假:忽略语句并继续执行后面的内容(转到第 5 步)
  3. 执行语句:
    cout << n << ", "; --n;(打印 n 的值并将 n 减 1)
  4. 块结束。自动返回第 2 步。
  5. 块后继续程序:打印 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 循环提供了特定的位置来包含 initializationincrease 表达式,分别在循环开始时和每次迭代后执行。因此,使用计数器变量作为 condition 特别有用。

它按以下方式工作:

  1. 执行 initialization。通常,这声明一个计数器变量,并将其设置为某个初始值。这在循环开始时执行一次。
  2. 检查 condition。如果为真,循环继续;否则,循环结束,跳过 statement,直接进入第 5 步。
  3. 执行 statement。通常,它可以是一个单一语句或一个用大括号 { } 括起来的块。
  4. 执行 increase,循环返回第 2 步。
  5. 循环结束:继续执行下一个语句。
    这是使用 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;) 是没有 initializationincrease 的循环(相当于 while 循环);for (;n<10;++n) 是带有 increase 的循环,但没有 initialization(也许是因为变量在循环之前已经初始化)。没有 condition 的循环相当于条件为 true 的循环(即无限循环)。

由于每个字段在循环生命周期的特定时间执行,在作为 initializationconditionstatement 执行多个表达式可能很有用。不幸的是,这些不是语句,而是简单的表达式,因此不能用块替换。作为表达式,它们可以使用逗号运算符(,):这个运算符是一个表达式分隔符,可以分隔通常只期望一个的多个表达式。例如,使用它,可以使 for 循环处理两个计数器变量,初始化并增加这两个变量:

for ( n=0, i=100 ; n!=i ; ++n, --i )
{
   // 在此执行的内容...
}

如果在循环中既不修改 n 也不修改 i,这个循环将执行 50 次:

image.png

n 以 0 值开始,i 以 100 值开始,条件是 n!=i(即 n 不等于 i)。因为每次迭代 n 增加 1,i 减少 1,循环的条件将在第 50 次迭代后变为假,此时 ni 均为 50。

基于范围的 for 循环

for 循环还有另一种语法,仅用于范围:

for ( declaration : range ) statement;

这种 for 循环遍历 range 中的所有元素,其中 declaration 声明了一个能够获取该范围中元素值的变量。范围是元素的序列,包括数组、容器和任何支持 beginend 函数的类型;这些类型大多数尚未在本教程中介绍,但我们已经熟悉至少一种范围:字符串,它们是字符序列。

使用字符串的基于范围的 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++ 常量表达式。

要检查范围或非常量值,最好使用连接的 ifelse if 语句。

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值