C 語言中 switch 語句的語法很簡單:
switch ( expression ) statement
C++ 繼承了 C 的 switch 並添加了添加可選 init-statement 的功能,但這不是本文的核心。
注意那裡不的內容:沒有提及 case 或 default。 這些在語法的其他地方指定。 這表示 switch 語句的正確性是在語意上而不是在語法上強制執行的。 這樣做的後果是聲明:
C 的一個有爭議的功能是,在 switch 語句中,案例「落入」下一個案例(如果有)。 例如,給定變數 c 的值為“a”,程式碼如下:
switch ( c ) { case 'a': printf( "apple\n" ); case 'b': printf( "banana\n" ); }
將列印 apple 和 香蕉,因為在匹配 'a' 並列印 apple 後,執行只是「落入」'b' 情況。 這是上述結果 #2 的奇怪結果,因為在 switch 之外,連續的語句自然會從一個語句「落入」下一個語句。在情況之間的切換內部,大多數時候這不是您想要的,因此您可以使用中斷(或者如果在循環、返回或轉到內部則繼續)。
大多數編譯器將允許您請求在程式碼陷入下一種情況時收到警告。 從 C23 或 C++17 開始,您可以包含 [[fallthrough]] 屬性來告訴編譯器,fallthrough 是故意的,而不是警告您:
switch ( how_good ) { case VERY_GOOD: printf( "very " ); [[fallthrough]]; case GOOD: printf( "good\n" ); break; }
也許最有名的關於跌倒有用的例子就是 Duff 的設備。 您可以在那裡閱讀它的詳細信息,但底線是代碼(用現代 C 重寫):
void send( short *to, short const *from, size_t count ) { size_t n = (count + 7) / 8; switch ( count % 8 ) { case 0: do { *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; } while ( --n > 0 ); } }
由於結果#3 的結果是完全合法的,即 do 迴圈位於 switch 內,允許 任何 語句具有 case 標籤。
使用switch 時,語句 總是一個複合語句,即包含在{} 中的一系列語句,但它也可以是單一 陳述:
bool check_n_args( int n_args ) { switch ( n_args ) // no { here case 0: case 1: case 2: return true; // no } here fprintf( stderr, "error: args must be 0-2\n" ); return false; }
由於只有一條 return true 語句,因此不需要 {},就像在 if、do、else、for 或 while 之後不需要它們一樣。
除了以上是另一種書寫方式之外:
if ( n_args >= 0 && n_args <= 2 ) return true;
(除了表達式只計算一次)沒有合理的理由使用帶有 switch 的單一語句,所以我從不建議這樣做。 這只是上面第 1 條後果的奇怪結果。
當開關有預設值時,它總是最後一個,但它實際上可以位於開關內的任何位置:
switch ( n_args ) { default: fprintf( stderr, "error: args must be 0-2\n" ); return false; case 0: // ...
就效能而言,預設的位置(或實際上是案例的順序)並不重要。 最後沒有預設的唯一技術原因是如果您希望執行落入下一個案例。 任何其他原因都純粹是風格上的,例如,您想先處理常見情況,然後處理特殊情況。
也可以在第一個case之前有語句,例如:
switch ( n_args ) { printf( "never executed\n" ); case 0: // ...
此類語句永遠不會執行。 大多數編譯器都會對此發出警告。 據我所知,沒有理由在第一個案例之前發表聲明。
但是,在第一個案例之前加上聲明是有一定用處的,例如:
switch ( n_args ) { int i; case 0: i = f(); // ... break; case 1: i = g(); // ... break; }
當變數僅在一種或多種情況下的 switch 範圍內使用時,這有點用處。 請注意,您不應該不初始化此類變量,例如:
switch ( n_args ) { int i = 0; // WRONG: do _not_ initialize! // ...
因為,即使變數被宣告,它的初始化程式碼永遠不會被執行(就像前面例子中的printf()永遠不會被執行一樣),所以代碼是具有欺騙性。 相反,您必須在每種使用這些變數的情況下初始化它們。
即使簡單的聲明(沒有初始化)不是可執行程式碼,一些編譯器仍然會(錯誤地,恕我直言)警告它們。 因此,這樣的聲明是沒有用的。
如果您確實只想在開關範圍內進行聲明,則可以將它們放在第一個案例中或僅放在使用它們的案例中。 但是,在 C23 之前,不允許在標籤後立即聲明:
switch ( n_args ) { case 0: int i; // error (pre-C23) // ...
要解決該限制,您可以為案例添加 {}:
case 0: { int i; // OK now (all C versions) // ... }
If you have a long block of code that you want to jump to the end of, there are a few ways to do it:
Each has its trade-offs. Another way would be:
#define BLOCK switch (0) default: void f() { BLOCK { // ... if ( condition_1 ) break; // ... lots more code ... } // "break" above jumps here
Hence, it’s most similar to do { ... } while (0), but without having to put the while (0) at the end.
The apparent simplicity of the switch statement in C (and C++) is deceptive in that it allows several odd ways to write code using them, some useful, some not. The most useful is Duff’s device for loop unrolling.
以上是Switch 語句的奇怪之處的詳細內容。更多資訊請關注PHP中文網其他相關文章!