分支、循環都講完了,現在只剩下最後一種影響控制流程的語句了,就是goto
語句,實現無條件跳轉。我們知道break
只能跳出最內層的循環,如果在一個嵌套循環中遇到某個錯誤條件需要立即跳出最外層循環做出錯處理,就可以用goto
語句,例如:
for (...) for (...) { ... if (出現錯誤條件) goto error; } error: 出錯處理;
這裡的error:
叫做標號(Label),任何語句前面都可以加若干個標號,每個標號的命名也要遵循標識符的命名規則。
goto
語句過于強大了,從程序中的任何地方都可以無條件跳轉到任何其它地方,只要在那個地方定義一個標號就行,唯一的限制是goto
只能跳轉到同一個函數中的某個標號處,而不能跳到別的函數中[11]。濫用goto
語句會使程序的控制流程非常複雜,可讀性很差。著名的計算機科學家Edsger W. Dijkstra最早指出編程語言中goto
語句的危害,提倡取消goto
語句。goto
語句不是必須存在的,顯然可以用別的辦法替代,比如上面的代碼段可以改寫為:
int cond = 0; /* bool variable indicating error condition */ for (...) { for (...) { ... if (出現錯誤條件) { cond = 1; break; } } if (cond) break; } if (cond) 出錯處理;
通常goto
語句只用於這種場合,一個函數中任何地方出現了錯誤條件都可以立即跳轉到函數末尾做出錯處理(例如釋放先前分配的資源、恢復先前改動過的全局變數等),處理完之後函數返回。比較用goto
和不用goto
的兩種寫法,用goto
語句還是方便很多。但是除此之外,在任何其它場合都不要輕易考慮使用goto
語句。有些編程語言(如C++)中有異常(Exception)處理的語法,可以代替goto
和setjmp/longjmp
的這種用法。
回想一下,我們在第 4 節 “switch語句”學過case
和default
後面也要跟冒號(:號,Colon),事實上它們是兩種特殊的標號。和標號有關的語法規則如下:
語句 → 標識符: 語句
語句 → case 常量表達式: 語句
語句 → default: 語句
反覆應用這些語法規則進行組合可以在一條語句前面添加多個標號,例如在例 4.2 “缺break的switch語句”的代碼中,有些語句前面有多個case
標號。現在我們再看switch
語句的格式:
switch (控製表達式) {
case 常量表達式: 語句列表
case 常量表達式: 語句列表
...
default: 語句列表
}
{}裡面是一組語句列表,其中每個分支的第一條語句帶有case
或default
標號,從語法上來說,switch
的語句塊和其它分支、循環結構的語句塊沒有本質區別:
語句 → switch (控製表達式) 語句
語句 → { 語句列表 }
有興趣的讀者可以在網上查找有關Duff's Device的資料,Duff's Device是一段很有意思的代碼,正是利用“switch
的語句塊和循環結構的語句塊沒有本質區別”這一點實現了一個巧妙的代碼優化。