首頁 > 後端開發 > C++ > 主體

Switch 語句的奇怪之處

王林
發布: 2024-09-06 06:51:31
原創
789 人瀏覽過

Switch Statement Oddities

介紹

C 語言中 switch 語句的語法很簡單:

        switch ( expression ) statement
登入後複製

C++ 繼承了 C 的 switch 並添加了添加可選 init-statement 的功能,但這不是本文的核心。

注意那裡的內容:沒有提及 case 或 default。 這些在語法的其他地方指定。 這表示 switch 語句的正確性是在語意上而不是在語法上強制執行的。 這樣做的後果是聲明

  1. 可以是任何語句。
  2. 被處理與任何其他語句完全相同
  3. 還可以包含零個或多個 case 標籤以及最多一個預設標籤。

跌倒

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)
        // ...
    }
登入後複製

A break-able Block

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:

  1. A sequence of if-else statements; or;
  2. A sequence of if-goto statements; or;
  3. A do { ... } while (0) statement with breaks.

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.

Conclusion

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中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!