語句和流程控制

一個簡單的 C++ 語句是程式中的每個獨立指令,例如前幾節中看到的變數宣告和表示式。它們總是以分號 (;) 結尾,並按照在程式中出現的順序執行。

但程式並不侷限於線性的語句序列。在處理過程中,程式可能會重複程式碼段,或者做出決策並分支。為此,C++ 提供了流程控制語句,用於指定程式在何時、何種情況下應該做什麼。

本節中解釋的許多流程控制語句都需要一個通用的(子)語句作為其語法的一部分。此語句可以是簡單的 C++ 語句,如以分號 (;) 結尾的單個指令,或者是複合語句。複合語句是由一組語句(每個語句都以自己的分號結尾)組成的,但它們都被包含在一個用花括號 {} 括起來的程式碼塊中。

{ statement1; statement2; statement3; }

整個程式碼塊被視為一個單一的語句(它本身由多個子語句組成)。無論何時,只要一個通用語句是流程控制語句語法的一部分,它都可以是簡單語句或複合語句。

選擇語句:if 和 else

if 關鍵字用於在條件滿足時執行語句或程式碼塊,僅在此情況下執行。其語法是:

if (condition) statement

這裡,condition 是正在被評估的表示式。如果此 condition 為真,則執行 statement。如果為假,則不執行 statement(僅忽略),程式將繼續執行整個選擇語句之後的程式碼。
例如,以下程式碼片段僅在 x 變數的值確實是 100 時才打印訊息 (x is 100)

1
2
if (x == 100)
  cout << "x is 100";

如果 x 不等於 100,則忽略此語句,不列印任何內容。

如果您想在滿足條件時包含多個語句,這些語句應包含在花括號 ({}) 中,形成一個程式碼塊。

1
2
3
4
5
if (x == 100)
{
   cout << "x is ";
   cout << x;
}

與往常一樣,程式碼中的縮排和換行沒有影響,因此上述程式碼等同於:

1
if (x == 100) { cout << "x is "; cout << x; }

帶有 if 的選擇語句也可以透過使用 else 關鍵字引入替代語句來指定在條件不滿足時會發生什麼。其語法是:

if (condition) statement1 else statement2

其中,如果 condition 為真,則執行 statement1;否則,執行 statement2

例如:

1
2
3
4
if (x == 100)
  cout << "x is 100";
else
  cout << "x is not 100";

如果 x 的值確實是 100,則列印 x is 100;如果不是,則列印 x is not 100
可以串聯多個 if + else 結構,以檢查值範圍。例如:

1
2
3
4
5
6
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。如果在執行 statement 之後,expression 不再為真,則迴圈結束,程式將繼續執行迴圈之後的程式碼。例如,讓我們來看一個使用 while 迴圈的倒計時:

// custom countdown using while
#include <iostream>
using namespace std;

int main ()
{
  int n = 10;

  while (n>0) {
    cout << n << ", ";
    --n;
  }

  cout << "liftoff!\n";
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!

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) 的值減一——這將在一定次數的迴圈迭代後,最終使條件 (n>0) 變為假。更具體地說,在 10 次迭代後,n 變為 0,使條件不再為真,while 迴圈也隨之結束。

請注意,對於計算機來說,此迴圈的複雜性非常簡單,因此整個倒計時是即時完成的,計數元素之間沒有實際延遲(如果您有興趣,請參閱 sleep_for 以瞭解帶有延遲的倒計時示例)。

do-while 迴圈

一個非常相似的迴圈是 do-while 迴圈,其語法是:

do statement while (condition);

它的行為與 while 迴圈相似,但不同之處在於 condition 是在 statement 執行之後而不是之前評估的,這保證了 statement 至少執行一次,即使 condition 從未滿足。例如,以下示例程式會回顯使用者輸入的任何文字,直到使用者輸入 goodbye 為止。

// echo machine
#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");
}
Enter text: hello
You entered: hello
Enter text: who's there?
You entered: who's there?
Enter text: goodbye
You entered: 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 迴圈的倒計時示例:

// countdown using a for loop
#include <iostream>
using namespace std;

int main ()
{
  for (int n=10; n>0; n--) {
    cout << n << ", ";
  }
  cout << "liftoff!\n";
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!

for 迴圈的三個欄位是可選的。它們可以留空,但在所有情況下,它們之間的分號是必需的。例如,for (;n<10;) 是一個沒有 *initialization* 或 *increase* 的迴圈(相當於 while 迴圈);for (;n<10;++n) 是一個有 *increase* 但沒有 *initialization* 的迴圈(可能因為變數在迴圈之前已被初始化)。沒有 *condition* 的迴圈等同於以 true 作為條件的迴圈(即無限迴圈)。

由於每個欄位都在迴圈生命週期的特定時間執行,因此將多個表示式作為 *initialization*、*condition* 或 *statement* 執行可能很有用。不幸的是,它們不是語句,而是簡單的表示式,因此不能被程式碼塊替換。作為表示式,它們可以使用逗號運算子 (,):此運算子是表示式分隔符,可以在通常只期望一個表示式的地方分隔多個表示式。例如,使用它,for 迴圈可以處理兩個計數器變數,同時初始化和增加它們:

1
2
3
4
for ( n=0, i=100 ; n!=i ; ++n, --i )
{
   // whatever here...
}

如果 ni 在迴圈中未被修改,此迴圈將執行 50 次。



n 從 0 開始,i 從 100 開始,條件是 n!=i(即 n 不等於 i)。由於每次迭代 n 增加一,i 減少一,因此在第 50 次迭代後,當 ni 都等於 50 時,迴圈的條件將變為假。

基於範圍的 for 迴圈

for 迴圈有另一種語法,專門用於範圍:

for ( declaration : range ) statement;

這種 for 迴圈遍歷 range 中的所有元素,其中 declaration 宣告一個變數,該變數可以儲存此範圍中某個元素的值。範圍是元素的序列,包括陣列、容器以及任何支援 beginend 函式的型別;本教程中尚未介紹這些型別中的大多數,但我們已經熟悉了至少一種範圍:字串,它們是字元序列。

使用字串的基於範圍的 for 迴圈示例:

// range-based for loop
#include <iostream>
#include <string>
using namespace std;

int main ()
{
  string str {"Hello!"};
  for (char c : str)
  {
    cout << "[" << c << "]";
  }
  cout << '\n';
}
[H][e][l][l][o][!]

請注意,在 for 迴圈中,冒號 (:) 前面是 char 變數的宣告(字串中的元素是 char 型別)。然後,我們在語句塊中使用該變數 c 來表示範圍中每個元素的值。

此迴圈是自動的,不需要顯式宣告任何計數器變數。

基於範圍的迴圈通常也使用 auto 進行型別推導來確定元素的型別。通常,上面的基於範圍的迴圈也可以這樣寫:

1
2
for (auto c : str)
  cout << "[" << c << "]";

在這裡,c 的型別是從 str 的元素型別自動推匯出來的。

跳轉語句

跳轉語句允許透過跳轉到特定位置來改變程式的流程。

break 語句

break 語句會退出迴圈,即使迴圈的結束條件尚未滿足。它可用於結束無限迴圈,或強制迴圈在其自然結束之前終止。例如,讓我們在自然結束之前停止倒計時:

// break loop example
#include <iostream>
using namespace std;

int main ()
{
  for (int n=10; n>0; n--)
  {
    cout << n << ", ";
    if (n==3)
    {
      cout << "countdown aborted!";
      break;
    }
  }
}
10, 9, 8, 7, 6, 5, 4, 3, countdown aborted!

continue 語句

continue 語句會導致程式跳過當前迭代中迴圈的其餘部分,就好像已到達語句塊的末尾一樣,使其跳轉到下一次迭代的開頭。例如,讓我們在倒計時中跳過數字 5:

// continue loop example
#include <iostream>
using namespace std;

int main ()
{
  for (int n=10; n>0; n--) {
    if (n==5) continue;
    cout << n << ", ";
  }
  cout << "liftoff!\n";
}
10, 9, 8, 7, 6, 4, 3, 2, 1, liftoff!

goto 語句

goto 允許程式跳轉到程式的另一個絕對點。這種無條件跳轉會忽略巢狀級別,也不會導致任何自動堆疊展開。因此,這是一項需要謹慎使用的功能,最好在同一語句塊內使用,尤其是在存在區域性變數的情況下。

目標點由一個 *標籤* 標識,該標籤隨後用作 goto 語句的引數。*標籤* 由一個有效的識別符號後跟一個冒號 (:) 組成。

goto 通常被認為是一種低階功能,在現代 C++ 使用的高階程式設計範例中沒有特別的用例。但是,僅作為示例,這裡有一個使用 goto 的倒計時迴圈版本:

// goto loop example
#include <iostream>
using namespace std;

int main ()
{
  int n=10;
mylabel:
  cout << n << ", ";
  n--;
  if (n>0) goto mylabel;
  cout << "liftoff!\n";
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, liftoff!

另一個選擇語句:switch。

switch 語句的語法有點特殊。它的目的是檢查多個可能的常量表達式中的一個值。它類似於串聯 if-else 語句,但僅限於常量表達式。其最典型的語法是:

code>switch (expression)
{
case constant1:
group-of-statements-1;
break;
case constant2:
group-of-statements-2;
break;
.
.
.
default:
default-group-of-statements
} /code>

它的工作方式是:switch 評估 expression 並檢查它是否等於 constant1;如果等於,則執行 group-of-statements-1,直到找到 break 語句。當找到該 break 語句時,程式將跳轉到整個 switch 語句的末尾(結束花括號)。

如果 expression 不等於 constant1,則會將其與 constant2 進行比較。如果相等,則執行 group-of-statements-2,直到找到 break,此時將跳轉到 switch 的末尾。

最後,如果 expression 的值與之前指定的任何常量都不匹配(這些常量可能數量不定),則程式將執行 default: 標籤之後的語句(如果存在,因為它是可選的)。

以下兩個程式碼片段具有相同的行為,展示了 switch 語句的 if-else 等效項:

switch 示例if-else 等效項
code>switch (x) {
case 1:
cout "x is 1";
break;
case 2:
cout "x is 2";
break;
default:
cout "value of x unknown";
} /code>
code>if (x == 1) {
cout "x is 1";
}
else if (x == 2) {
cout "x is 2";
}
else {
cout "value of x unknown";
}
/code>

switch 語句的語法有些特殊,這是從早期 C 編譯器繼承下來的,因為它使用標籤而不是程式碼塊。在最典型的用法(如上所示)中,這意味著需要為每個標籤後面的語句組新增 break 語句。如果省略 break,則會繼續執行 case 後面的所有語句(包括其他標籤下的語句),直到 switch 程式碼塊結束或遇到跳轉語句(如 break)。

如果上面的示例在 case one 的第一個組之後缺少 break 語句,程式不會自動跳轉到 switch 程式碼塊的末尾,而是會繼續執行 case two 中的語句(因此也會列印 x is 2)。它將繼續這樣做,直到遇到 break 語句或 switch 程式碼塊的末尾。這使得為每個 case 的語句包含在花括號 {} 中成為不必要的,並且也可以用於為不同的可能值執行相同的語句組。例如:

1
2
3
4
5
6
7
8
9
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 語句的串聯。
Index
目錄