コンテンツにスキップ

C言語/fallthrough

出典: フリー教科書『ウィキブックス(Wikibooks)』

はじめに

[編集]

C言語のプログラマにとって、switch文におけるフォールスルー(あるケースから次のケースへの制御の移行)は、バグの原因となることが少なくありません。C23では、この問題に対処するためにfallthrough属性が導入されました。本章では、この新機能について詳しく解説します。

fallthrough属性の概要

[編集]

C23では、開発者の意図を明確にし、意図しないフォールスルーによるバグを防ぐために、fallthrough属性が導入されました。この属性は、switch文のcaseラベルまたはdefaultラベルの後の文に適用され、次のケースへの意図的なフォールスルーを明示します。

基本構文

[編集]
[[fallthrough]];

この属性は単独の文(空文)として使用され、次のラベルへのフォールスルーが意図的であることをコンパイラとコードレビュアーに示します。

使用例

[編集]

伝統的なフォールスルー

[編集]

従来のCコードでは、フォールスルーは次のように書かれていました:

switch (value) {
    case 1:
        foo();
        /* フォールスルー(意図的か否かは不明)*/
    case 2:
        bar();
        break;
    default:
        baz();
}

C23での明示的フォールスルー

[編集]

C23では、意図的なフォールスルーを明示的に示すことができます:

switch (value) {
    case 1:
        foo();
        [[fallthrough]]; /* 意図的なフォールスルーであることを明示 */
    case 2:
        bar();
        break;
    default:
        baz();
}

コンパイラの警告制御

[編集]

多くの現代的なCコンパイラは、switch文における暗黙的なフォールスルーに対して警告を発することがあります。fallthrough属性を使用することで、これらの警告を抑制し、コードの意図を明確にできます。

以下は、コンパイラの警告動作をまとめた表です:

シナリオ fallthroughなし fallthroughあり
意図的なフォールスルー 警告(多くのコンパイラ) 警告なし
break忘れ 警告(多くのコンパイラ) 該当なし
空のケース 通常警告なし 不要(既に明示的)

実践的な使用例

[編集]

状態遷移における使用

[編集]
enum State { INIT, READY, RUNNING, PAUSED, STOPPED };

void process_state(enum State current_state) {
    switch (current_state) {
        case INIT:
            initialize_system();
            [[fallthrough]]; /* 初期化後、READYと同じ処理を行う */
        case READY:
            prepare_execution();
            break;
        case RUNNING:
            execute_tasks();
            break;
        case PAUSED:
            save_state();
            break;
        case STOPPED:
            cleanup_resources();
            break;
    }
}

複数のケースで共通処理を行う場合

[編集]
void process_command(int cmd) {
    switch (cmd) {
        case CMD_SAVE:
            save_data();
            [[fallthrough]]; /* 保存後に表示も更新 */
        case CMD_REFRESH:
            update_display();
            break;
        case CMD_EXIT:
            cleanup();
            break;
        default:
            report_unknown_command(cmd);
    }
}

コンパイラ互換性

[編集]

fallthrough属性は標準C23の一部ですが、古いバージョンのCコンパイラでも同様の機能がサポートされていることがあります。以下は主要コンパイラの互換性情報です:

コンパイラ サポート状況 備考
GCC GCC 7以降 attribute((fallthrough)) として以前から存在
Clang Clang 3.9以降 C++17互換のfallthroughattribute((fallthrough))
MSVC Visual Studio 2017以降 C++17互換のfallthroughをサポート
ICC (Intel) ICC 19.0以降 C++17互換として部分的にサポート

移植性を考慮したコード

[編集]

C23以前のコンパイラとの互換性を保ちながらfallthrough属性を使用する方法:

#if __STDC_VERSION__ >= 202311L
    #define FALLTHROUGH [[fallthrough]]
#elif defined(__GNUC__) && __GNUC__ >= 7
    #define FALLTHROUGH __attribute__((fallthrough))
#elif defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201703L
    #define FALLTHROUGH [[fallthrough]]
#else
    #define FALLTHROUGH /* fallthrough */
#endif
switch (value) {
    case 1:
        foo();
        FALLTHROUGH;
    case 2:
        bar();
        break;
}

まとめ

[編集]

C23のfallthrough属性は、switch文における意図的なフォールスルーを明示的に示すことで、コードの可読性を向上させ、バグを防止するための重要な機能です。この属性を適切に使用することで、コードの意図がより明確になり、コンパイラ警告を適切に制御できるようになります。

実際のプログラミングでは、フォールスルーが必要な場合にfallthroughを使用することで、コードの品質と保守性が向上するでしょう。特に複数の開発者が関わる大規模プロジェクトでは、この明示的な表記が意図しないバグを防ぐ助けとなります。