code-prettify

2019年8月8日 星期四

C 與 C++ 中 switch case 不能宣告變數的真相

今天在工作上,同事遇到了 switch case 中,宣告變數會造成編譯錯誤的問題,其實只要在 label 後的 code 加上 {} 把 statement 放到新的 scope 即可,但是仔細研究後發現,雖然在 C 與 C++ 中都會編譯錯誤,但是編譯錯誤的訊息跟點是不一樣的,因此用這篇文章記錄一下查到的資料與心得。

C++


編譯錯誤會發生在第 11 行,g++ 噴出的錯誤訊息是

 11:9: error: jump to case label
 11 | default:
      | ^~~~~~~
 9:17: note: crosses initialization of 'int i'
 9   | int i = 0;

這表示的是 i 在 case label 0 的 scope 裡面有被宣告並同時初始化(declaration with initialization),根據 C++ standard 6.7.3,
declaration statement 在 declaration with initialization 的區域變數是無法自動帶進其他 block ( switch 其實就是多個 {label, block} + goto 組成)。

讓它在 C++ compiler 編譯過的其中一個方法是,根據標準,不使用 declaration with initialization,而是把宣告與定義分開,即:

但是這樣子在 C code 是依然是無法編譯過的,因為在 C 跟 C++ 中的編譯錯誤的原因不同,接下來看看 C 的情況。

C


編譯錯誤會發生在第 9 行, gcc 噴出的錯誤訊息是

:9:13: error: a label can only be part of a statement and a declaration is not a statement
 9 | int i = 0;
    | ^~~
Compiler returned: 1

根據 C standard 6.8.1,declaration 並不是 statement,只有 statement 可以被 labeled,所以會編譯錯誤,注意 C 並不禁止在 block 裡使用宣告過但未初始化的變數。

一個修正方法是,在 label 後加上 empty statement,即


不過這樣在 C++ 依然不會編譯過。

然而在 C 與 C++ 同時可以編譯過的實作中,如

可編譯過的原因也是不同的:


  • C: {...} 為 compound statement (即 block),故可以被 labeled
  • C++: 透過 block 將 local 變數的 scope 限制在 labeled block 裡。

參考資料:

沒有留言:

張貼留言