Code Fix

中級

switch文でbreakを忘れて後続のcaseまで実行されるバグ

switch文の各case節末尾にbreakを書き忘れ、該当したcase以降のすべての節が続けて実行されてしまうバグです。

なぜエラーが出ないのか

出力: Mon\nTue
(エラーなし)
C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: Mon\nTue
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <iostream>
 2  int main() {
 3      int day = 1;
 4      switch (day) {
 5          case 1:
 6              std::cout << "Mon" << std::endl;
 7                       
                    ^
 8          case 2:
 9              std::cout << "Tue" << std::endl;
10              break;
11          case 3:
12              std::cout << "Wed" << std::endl;
13              break;
14      }
15      return 0;
16  }
出力: Mon\nTue期待: Mon のみ

case 1の末尾にbreakが無いため、一致したcase 1の処理が終わったあとも止まらずcase 2まで続けて実行されてしまいます。

直し方: (空) を break; にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <iostream>
 2  int main() {
 3      int level = 1;
 4      switch (level) {
 5          case 1:
 6              std::cout << "low" << std::endl;
 7                       
                    ^
 8          case 2:
 9              std::cout << "mid" << std::endl;
10              break;
11          case 3:
12              std::cout << "high" << std::endl;
13              break;
14      }
15      return 0;
16  }
出力: low\nmid期待: low のみ

case 1の末尾にbreakが無いため、一致したcase 1の処理が終わったあとも止まらずcase 2まで続けて実行されてしまいます。

直し方: (空) を break; にします。

この問題を解いてみる →

パターン3

 1  #include <iostream>
 2  int main() {
 3      int grade = 1;
 4      switch (grade) {
 5          case 1:
 6              std::cout << "A" << std::endl;
 7                       
                    ^
 8          case 2:
 9              std::cout << "B" << std::endl;
10              break;
11          case 3:
12              std::cout << "C" << std::endl;
13              break;
14      }
15      return 0;
16  }
出力: A\nB期待: A のみ

switchはbreakに出会うかブロックの終わりに達するまで、上から下へすべての文を実行し続けます(フォールスルー)。

直し方: (空) を break; にします。

この問題を解いてみる →

パターン4

 1  #include <iostream>
 2  int main() {
 3      int mode = 1;
 4      switch (mode) {
 5          case 1:
 6              std::cout << "start" << std::endl;
 7                       
                    ^
 8          case 2:
 9              std::cout << "run" << std::endl;
10              break;
11          case 3:
12              std::cout << "stop" << std::endl;
13              break;
14      }
15      return 0;
16  }
出力: start run期待: start のみ

case 1の末尾にbreakが無いため、一致したあともcase 2まで続けて実行されてしまいます。

直し方: (空) を break; にします。

この問題を解いてみる →

パターン5

 1  #include <iostream>
 2  int main() {
 3      int step = 1;
 4      switch (step) {
 5          case 1:
 6              std::cout << "first" << std::endl;
 7                       
                    ^
 8          case 2:
 9              std::cout << "second" << std::endl;
10              break;
11          case 3:
12              std::cout << "third" << std::endl;
13              break;
14      }
15      return 0;
16  }
出力: first second期待: first のみ

switchはbreakに出会うかブロックの終わりに達するまで実行し続けます。

直し方: (空) を break; にします。

この問題を解いてみる →

よくある誤解

「一致したcaseの中身だけが実行される」というのは誤解です。C++のswitchはbreakに出会うかswitchブロックの終わりに達するまで、上から下へすべての文を実行し続けます(フォールスルー)。

実務での勘所

意図的なフォールスルーであることをコンパイラと読み手の両方に伝える手段として、C++17から[[fallthrough]];という標準の属性が使えるようになりました。caseの最後にこれを書いておけば、-Wimplicit-fallthroughのような警告を有効にしていても「これは意図的です」と伝わり警告されなくなります。C++17より前はコンパイラごとの独自拡張に頼る必要がありましたが、現在は標準の書き方に統一されています。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)