C言語プログラミング:意図したフォールスルーを見逃さない!「[[fallthrough]]」属性の活用法


フォールスルーとは、switch 文の各 case ラベルの後に break 文を記述せずに、次の case ラベルへ処理が自動的に遷移することを指します。意図的に使用される場合もありますが、誤って記述してしまうと、本来の意図と異なるプログラム動作を引き起こすバグの原因となります。

「[[fallthrough]]」属性の役割

従来、意図したフォールスルーを記述する場合、コンパイラによっては警告が発せられていました。しかし、プログラマが意図してフォールスルーを行っている場合は、コンパイラの警告は不要です。そこで、C++17では「[[fallthrough]]」属性を導入し、意図したフォールスルーであることを明示的に伝えることができるようになりました。

「[[fallthrough]]」属性の書き方

「[[fallthrough]]」属性は、フォールスルーしたい case ラベルの最後のステートメントとして記述します。

switch (variable) {
  case 1:
    // 処理1を実行
    [[fallthrough]]; // 次の case ラベルへフォールスルー
  case 2:
    // 処理2を実行
  default:
    // デフォルト処理を実行
}

注意点

  • 末尾にセミコロン ; を記述する必要があります。
  • 「[[fallthrough]]」属性は、最後の case ラベルまたは default ラベルには記述できません。

「[[fallthrough]]」属性を使用する利点

  • コンパイラの警告を抑制し、不要な情報に惑わされることなくコードレビューに集中できます。
  • 意図したフォールスルーであることを明示的に示せるため、コードの可読性が向上します。

「[[fallthrough]]」属性は、C++ プログラマーにとって便利なツールです。意図したフォールスルーを明確に記述し、コードの可読性と保守性を向上させるために活用しましょう。

  • C++20では、[[fallthrough]] 属性に加えて、[[nodiscard]] 属性も導入されました。詳細は、C++20 の仕様を参照してください。
  • C言語では、[[fallthrough]] 属性はサポートされていません。


例1:連続する case ラベルへのフォールスルー

この例では、switch 文において、case 1case 2 の処理を連続して実行します。

#include <iostream>

using namespace std;

int main() {
  int value = 2;

  switch (value) {
    case 1:
      cout << "value は 1 です" << endl;
      [[fallthrough]]; // 次の case ラベルへフォールスルー
    case 2:
      cout << "value は 2 です" << endl;
      break;
    default:
      cout << "value は 1 または 2 ではありません" << endl;
  }

  return 0;
}

このコードを実行すると、以下の出力が得られます。

value は 1 です
value は 2 です

例2:default ラベルへのフォールスルー

この例では、switch 文において、case ラベルに一致しない場合でも、default ラベルの処理を実行します。

#include <iostream>

using namespace std;

int main() {
  int value = 3;

  switch (value) {
    case 1:
      cout << "value は 1 です" << endl;
      break;
    case 2:
      cout << "value は 2 です" << endl;
      break;
    default:
      cout << "value は 1 または 2 ではありません" << endl;
      [[fallthrough]]; // default ラベルへフォールスルー
      cout << "さらに処理を実行します" << endl;
  }

  return 0;
}
value は 1 または 2 ではありません
さらに処理を実行します

例3:複数の case ラベルへのフォールスルー

この例では、switch 文において、複数の case ラベルを連続して処理します。

#include <iostream>

using namespace std;

int main() {
  int value = 4;

  switch (value) {
    case 1:
    case 2:
      cout << "value は 1 または 2 です" << endl;
      [[fallthrough]]; // 次の case ラベルへフォールスルー
    case 3:
      cout << "value は 3 です" << endl;
      break;
    default:
      cout << "value は 1、2、または 3 ではありません" << endl;
  }

  return 0;
}
value は 1 または 2 です
value は 3 です

これらの例は、C++における「[[fallthrough]]」属性の使用方法を理解するためのものです。実際のプログラム開発においては、状況に応じて適切に使い分けることが重要です。



C言語で「[[fallthrough]]」属性の代替方法としては、以下の2つが考えられます。

goto 文を使用する

伝統的な方法として、goto 文を使用して次の case ラベルへジャンプする方法があります。

switch (variable) {
  case 1:
    // 処理1を実行
    goto case 2; // 次の case ラベルへジャンプ
  case 2:
    // 処理2を実行
  default:
    // デフォルト処理を実行
}

複合条件を用いる

最近のコンパイラでは、switch 文の case ラベルにカンマ区切りの式を記述することが許可されています。この機能を利用して、複数の case ラベルをグループ化し、条件に応じて処理を実行することができます。

switch (variable) {
  case 1, 2:
    // 処理1と処理2をまとめて実行
    break;
  default:
    // デフォルト処理を実行
}

それぞれの方法の利点と欠点

方法利点欠点
gotoシンプルでわかりやすいコードの可読性が低下する可能性がある
複合条件コード的可読性が高いすべてのコンパイラでサポートされているわけではない

C言語における「[[fallthrough]]」属性の代替方法としては、goto 文と複合条件の2つが考えられます。それぞれの方法の利点と欠点を理解した上で、状況に応じて適切な方法を選択することが重要です。

  • C++20では、[[fallthrough]] 属性に加えて、[[nodiscard]] 属性も導入されました。詳細は、C++20 の仕様を参照してください。