【C++】breakを書かないとどうなる?|switch文のフォールスルーを完全解説

サムネイル画像 C++

C++のswitch文を書いていて、「breakって毎回書かないとダメなの?」「忘れたらどうなるの?」と疑問に思ったことはありませんか?

実は、breakを忘れると次のcaseまで実行されてしまい、予期しないバグの原因になります。一方で、この挙動を意図的に使うテクニックもあります。

この記事を読み終えると、あなたはbreakの役割とフォールスルーの仕組みを完全に理解できると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

breakとは?

breakとは、switch文やループから抜け出すためのキーワードです。

switch文では、breakを書かないと次のcaseにそのまま流れてしまいます。これをフォールスルー(fall through)と呼びます。

まずは、breakがある場合とない場合を比較してみましょう。

breakがある場合(正常動作)

▼main.cpp

#include <iostream>

int main() {
    int cmd = 2;
    
    switch (cmd) {
        case 1:
            std::cout << "新規作成\n";
            break; // ← ここでswitch文から抜ける
        case 2:
            std::cout << "開く\n";
            break; // ← ここでswitch文から抜ける
        case 3:
            std::cout << "保存\n";
            break;
        default:
            std::cout << "無効なコマンド\n";
    }
    
    std::cout << "処理完了\n";
    return 0;
}

実行結果

開く
処理完了

case 2の処理だけが実行され、breakでswitch文から抜けて次の行に進みます。

breakがない場合(フォールスルー発生)

では、breakを書き忘れるとどうなるでしょうか?

▼main.cpp

#include <iostream>

int main() {
    int cmd = 2;
    
    switch (cmd) {
        case 1:
            std::cout << "新規作成\n";
            // breakがない!
        case 2:
            std::cout << "開く\n";
            // breakがない!
        case 3:
            std::cout << "保存\n";
            break;
        default:
            std::cout << "無効なコマンド\n";
    }
    
    std::cout << "処理完了\n";
    return 0;
}

実行結果

開く
保存
処理完了

case 2だけでなく、case 3まで実行されてしまいました!

これがフォールスルーです。breakがないと、条件に関係なく次のcaseに流れ込んでしまいます。

フォールスルーの危険性

フォールスルーは意図しないバグの原因になります。

▼main.cpp

#include <iostream>

int main() {
    int level = 2;
    int damage = 0;
    
    switch (level) {
        case 1:
            damage = 10;
            // breakを忘れた!
        case 2:
            damage = 20;
            // breakを忘れた!
        case 3:
            damage = 30;
            break;
    }
    
    std::cout << "ダメージ: " << damage << "\n";
    return 0;
}

実行結果

ダメージ: 30

level = 2なのに、ダメージが30になってしまいました。

本来はdamage = 20で終わるはずが、breakがないためcase 3も実行され、damage = 30で上書きされてしまったのです。

【重要】私が実際にbreakで困った体験談

体験談1: ゲームのメニューが暴走した

勉強として「RPGのメニュー画面」を作っていた時のことです。以下のようなコードを書きました。

int choice = 1; // 1=アイテム, 2=装備, 3=ステータス

switch (choice) {
    case 1:
        std::cout << "アイテムを開く\n";
        // breakを忘れた!
    case 2:
        std::cout << "装備を開く\n";
        // breakを忘れた!
    case 3:
        std::cout << "ステータスを開く\n";
        break;
}

「アイテム」を選んだのに、「アイテムを開く」「装備を開く」「ステータスを開く」すべてが実行されてしまいました。

アイテムを開く
装備を開く
ステータスを開く

デバッグに30分以上かかり、原因がbreakの書き忘れだと気づいた時は絶望しました。この経験から、switch文ではbreakを必ず書く習慣がつきました。

体験談2: 敵のAIが全パターン実行してしまった

個人開発でアクションゲームを作っていた時、敵のAI行動を以下のように実装しました。

int aiState = 1; // 1=巡回, 2=追跡, 3=攻撃

switch (aiState) {
    case 1:
        std::cout << "巡回中...\n";
        // breakを書き忘れた!
    case 2:
        std::cout << "プレイヤーを追跡!\n";
        // breakを書き忘れた!
    case 3:
        std::cout << "攻撃!\n";
        break;
}

敵が「巡回」状態なのに、「巡回→追跡→攻撃」すべてが1フレームで実行されてしまいました。

巡回中...
プレイヤーを追跡!
攻撃!

テストプレイヤーから「敵がワープして攻撃してくる」とバグ報告が届き、原因を調べたらbreakの書き忘れでした。switch文は1文字のミスで大きなバグになると痛感しました。

フォールスルーを意図的に使う場合

フォールスルーは危険ですが、意図的に使うと便利な場面もあります。

例1: 複数のcaseで同じ処理をする

▼main.cpp

#include <iostream>

int main() {
    char grade = 'B';
    
    switch (grade) {
        case 'A':
        case 'B':
        case 'C':
            std::cout << "合格\n";
            break;
        case 'D':
        case 'F':
            std::cout << "不合格\n";
            break;
        default:
            std::cout << "無効な成績\n";
    }
    
    return 0;
}

実行結果

合格

これは「A, B, C のどれかなら合格」という意味です。フォールスルーを活用した正しい書き方です。

例2: 曜日判定

▼main.cpp

#include <iostream>

int main() {
    int dayOfWeek = 6; // 1=月, 2=火, ..., 6=土, 7=日
    
    switch (dayOfWeek) {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
            std::cout << "平日\n";
            break;
        case 6:
        case 7:
            std::cout << "週末\n";
            break;
        default:
            std::cout << "無効な曜日\n";
    }
    
    return 0;
}

実行結果

週末

このように、複数のcaseで同じ処理をしたい時はフォールスルーが便利です。

よくある失敗例と対処法

失敗例1: 最後のcaseだけbreakを書き忘れた

症状: 最後のcaseからdefaultに流れてしまう
原因: 最後のcaseにもbreakが必要
対処法: すべてのcaseにbreakを書く習慣をつける

// NG
switch (cmd) {
    case 1:
        std::cout << "処理1\n";
        break;
    case 2:
        std::cout << "処理2\n";
        // breakがない!
    default:
        std::cout << "無効\n"; // ← ここも実行される
}

// OK
switch (cmd) {
    case 1:
        std::cout << "処理1\n";
        break;
    case 2:
        std::cout << "処理2\n";
        break; // ← 必ず書く
    default:
        std::cout << "無効\n";
}

失敗例2: フォールスルーを使ったがコメントがない

症状: 他の人が「breakの書き忘れ」だと勘違いする
原因: 意図的なフォールスルーだとわからない
対処法: コメントで明示する

// NG(意図が不明)
switch (grade) {
    case 'A':
    case 'B':
        std::cout << "合格\n";
        break;
}

// OK(コメントで明示)
switch (grade) {
    case 'A':
    case 'B': // フォールスルー: AとBは同じ処理
        std::cout << "合格\n";
        break;
}

// または [[fallthrough]] 属性を使う(C++17以降)
switch (grade) {
    case 'A':
        [[fallthrough]]; // 意図的なフォールスルー
    case 'B':
        std::cout << "合格\n";
        break;
}

失敗例3: defaultにbreakを書いていない

症状: defaultの後にコードがあると実行されてしまう
原因: defaultも他のcaseと同じくフォールスルーする
対処法: defaultにもbreakを書く(またはreturnで終える)

// NG
switch (cmd) {
    case 1:
        std::cout << "処理1\n";
        break;
    default:
        std::cout << "無効\n";
        // breakがない!
}
std::cout << "ここも実行される\n"; // switch外だが、defaultから流れる

// OK
switch (cmd) {
    case 1:
        std::cout << "処理1\n";
        break;
    default:
        std::cout << "無効\n";
        break; // ← 書く
}
std::cout << "正常に実行\n";

breakとreturnの違い

switch文では、breakの代わりにreturnを使うこともできます。

▼main.cpp

#include <iostream>

int getPrice(int itemID) {
    switch (itemID) {
        case 1:
            return 100; // breakの代わりにreturn
        case 2:
            return 200;
        case 3:
            return 300;
        default:
            return 0;
    }
}

int main() {
    std::cout << "価格: " << getPrice(2) << "円\n";
    return 0;
}

実行結果

価格: 200円

returnは関数自体を終了するため、breakよりも強力です。関数内のswitch文ならreturnを使う方がシンプルです。

break vs return 比較表

比較項目breakreturn
動作switch文から抜ける関数自体を終了
使える場所switch, ループ関数内のどこでも
switch後の処理実行される実行されない
値の返却不可可能
おすすめswitch後に処理がある時値を返す関数の中

注意点

  • ⚠️ break忘れは頻出バグ: コンパイラは警告を出さないことが多い
  • ⚠️ すべてのcaseにbreakを書く: 最後のcaseもdefaultも例外なし
  • ⚠️ 意図的なフォールスルーはコメント必須: [[fallthrough]]// フォールスルーと明示
  • ⚠️ returnで代用可能: 関数内ならreturnの方がシンプル
  • ⚠️ デバッグ時は真っ先に疑う: 予期しない動作ならbreakを確認

まとめ

breakの重要性をまとめます。

  • breakがないと次のcaseも実行される(フォールスルー)
  • フォールスルーは意図しないバグの原因になる
  • すべてのcaseにbreakを書くのが基本
  • 複数caseで同じ処理をする時はフォールスルーが便利
  • 意図的なフォールスルーはコメント必須([[fallthrough]]推奨)
  • 関数内ならreturnで代用可能
  • デバッグ時は真っ先にbreakを疑う

これでbreakの役割とフォールスルーの仕組みはバッチリですね!

switch文を書く時は、必ずbreakを書く習慣をつけましょう。

ここまで読んでくださり、ありがとうございました。

この記事が皆様の学習に役立てば幸いです。

関連記事