C語言跳轉語句(break語句,continue語句,goto語句,return語句)

2020-07-16 10:04:20
跳轉語句(jump statement)可以中斷當前程式的執行流程,並從另一個不同的點繼續執行程式。如果程式跳轉到變數的作用域範圍之外,變數會被銷毀。C 語言有四種語句會造成無條件跳轉:break、continue、goto 和 return。

break 語句

break 語句只能用於迴圈體內或 switch 語句內,並且會使得程式流跳轉到該迴圈或該 switch 語句後面的第一條語句。break 語句的語法如下:

break;


因此,無論在迴圈體內什麼位置,break 語句都可以造成迴圈的結束。例如,例 1 的 while 迴圈可以依據使用者的請求而結束(輸入非數位的字串),也可能因為數位超出程式許可範圍而結束。

【例1】break 語句
// 讀取使用者輸入的分數,範圍在0到100之間
// 將分數儲存在陣列中
// 返回值:所儲存值的個數
//---------------------------------------------------------------
int getScores( short scores[ ], int len )
{
   int i = 0;
   puts( "Please enter scores between 0 and 100.n"
         "Press <Q> and <Return> to quit.n" );
   while ( i < len )
   {
      printf( "Score No. %2d: ", i+1 );
      if ( scanf( "%hd", &scores[i] ) != 1 )
         break;      // 未讀到資料:結束回圈
      if ( scores[i] < 0 || scores[i] > 100 )
      {
         printf( "%d: Value out of range.n", scores[i] );
         break;      // 拋棄這個值,並結束回圈
       }
       ++i;
   }
   return i;         // 已儲存的資料個數
}

continue 語句

continue 語句只能在迴圈體內使用,並且會造成程式流跳過當前迴圈中尚未執行的程式碼部分。它的語法如下:

continue;


在 while 或 do...while 迴圈中,當遇到 continue 語句時,程式會跳轉到回圈的控制表示式,並進行下一次的迴圈條件計算。在 for 迴圈中,程式會跳轉到回圈頭部的第三個表示式,並進行下一次的迴圈條件計算。

在例 1 中,一旦輸入值超出許可範圍,第二條 break 語句會立即中止資料輸入迴圈。為了讓使用者還有機會輸入正確的值,把第二條 break 語句用 continue 取代。那麼這個程式就會跳轉到 while 迴圈的下一次迴圈,忽略自增 i 的語句:
// 讀取分數
// --------------------------
int getScores( short scores[ ], int len )
{
   /* ... (同例6-7) ... */
   while ( i < len )
   {
      /* ... (同例6-7) ... */
      if ( scores[i] < 0 || scores[i] > 100 )
      {
         printf( "%d : Value out of range.n", scores[i] );
         continue;             // 拋棄這個值,並讀取另一個值
      }
      ++i;                     // 已儲存的資料個數加1
   }
   return i;           // 已儲存的資料個數
}

goto 語句

goto 語句會造成無條件跳轉,它跳轉到同一個函數中的另一條語句。跳轉的目的地使用標籤名稱來指定,語法如下:

goto 標籤名稱;


一個標籤由標籤名稱及其後面的冒號組成:

標籤名稱: 語句


標籤有自己的名稱空間,也就是說,標籤可以使用與變數或型別一樣的名稱,而不會發生衝突。標籤可以被放在任何語句的前面,並且一條語句也可以有多個標籤。

標籤的目的是標識 goto 語句的目的地,對於語句本身,沒有任何影響,被貼上標籤的語句依然可以由上而下順序地執行。下面的函數在 return 語句後面加上了標籤,標記了一個錯誤處理程式的進入點:
// 在函數內部處理錯誤
// ----------------------------------
#include <stdbool.h>                            // 定義布林值,true和false(C99)
#define MAX_ARR_LENGTH 1000
bool calculate( double arr[ ], int len, double* result )
{
   bool error = false;
   if ( len < 1 || len > MAX_ARR_LENGTH )
     goto error_exit;
   for ( int i = 0; i < len; ++i )
   {
     /* ... 一些計算操作,其可能造成錯誤標誌error被設定...
      */
     if ( error )
        goto error_exit;
     /* ... 繼續計算;結果被儲存到變數 *result 中...
      */
   }
   return true;                               // 如果沒有錯誤,程式會執行到此處

   error_exit:                        // 錯誤處理子程式
   *result = 0.0;
   return false;
}

如果跳轉會跨越變數的宣告與初始化語句,那麼就不應該利用goto語句從語句塊外跳轉到語句塊內。然而,如果跳轉跨越了對可變長度陣列的定義,而跳到了其作用域的內部,那麼這種跳躍是非法的:
static const int maxSize = 1000;
double func( int n )
{
   double x = 0.0;
   if ( n > 0 && n < maxSize )
   {
      double arr[n];                      // 一個變長度陣列
      again:
      /* ... */
      if ( x == 0.0 )
        goto again;                           // 合法:在arr的作用域內跳轉
   }
   if ( x < 0.0 )
      goto again;                             // 非法: 從arr的作用域外跳轉到作用域內

   return x;
}

如果使用太多 goto 語句,程式程式碼會變得可讀性很差,因此,只有在非常有必要時才應該使用 goto 語句,比如從很深的巢狀循壞中跳離。實際上,在任何使用到 goto 語句的地方,都可以採用其他方式的語句進行改寫。

goto 語句只允許進行區域性跳轉:也就是在當前所在函數的內部跳轉。C 語言還提供了一個特性,允許進行非區域性跳轉,即可以跳轉到程式的任何點,做法是利用標準宏 setjmp()和標準函數 longjmp()。

宏 setjmp()在程式中設定一個地點,將程式流的必要處理資訊儲存起來,這樣的話,當呼叫函數 longjmp()時,就可以在任何時刻返回到該地點繼續執行。

return 語句

return 語句會中止執行當前函數,跳轉回到呼叫該函數的位置

return [表示式];


這裡的表示式會被計算,且結果會被傳送給函數呼叫者,當作被呼叫函數的返回值。如有必要,返回值會被轉換到被呼叫函數的返回值型別。

一個函數內可以有任意多個 return 語句:
// 返回兩個整數型別引數中的較小值
int min( int a, int b )
{
   if ( a < b ) return a;
   else             return b;
}

該函數體內的 if else 語句可以用下面這一條語句來替代:
return ( a < b ? a : b );

括號不會影響 return 語句的執行行為。然而,複雜的 return 表示式常常被放在括號內,以提高程式碼的可閱讀性。

不帶任何表示式的 return 語句僅能在型別為 void 的函數中使用。事實上,這樣的函數也根本不需要 return 語句。如果在函數內沒有 return 語句,程式流會在函數塊尾部結束,然後返回到呼叫該函數的地方。