指示付きの初期化子

こんにちは、めのんです!

今回からこれまでとは違ってCに関するいろんな話題を取り上げていきたいと考えています。
初心者向けの初歩的な話題もあれば、ベテランの方でも意外にご存じない話題までいろいと取り上げていきますので、どうぞよろしくお願いします!

初回は「指示付きの初期化子」についてです。

まずは「指示付きの初期化子」がどんなものなのか、実際のコードをご覧ください。

int a[] =
{
  [3] = 12,
  [7] = 100,
  [1] = 4,
};

配列の初期化子は、普通なら要素を順に並べていくだけですよね。
でも、指示付きの初期化子では上の例のように

要素指示子 = 初期値

のように、「要素指示子」を使って特定要素の初期値を書くことができます。
要素指示子は配列であれば [3] のように書きます。

要素指示子は全要素分を書く必要はありません。
初期値を指定しなかった要素にはゼロが埋められます。

指示付きの初期化子は配列だけではなく、構造体や共用体にも使うことができます。

struct A
{
  int a, b, c;
};

struct A x =
{
  .b = 12,
  .a = 5,
};

構造体や共用体のメンバを指定する要素指示子は .a のようにドットのあとにメンバ名を書きます。

指示付きの初期化子の優れたところは、具体的な要素を指定して初期値を書けること以外に、順不同で書くことができる点があります。
共用体の場合はこれがとくに重要で、普通の書き方であれば共用体の先頭メンバに対してしか初期値を設定できなかったのが、指示付きの初期化子を使うことで2番目以降のメンバに初期値を設定できます。

union B
{
  int a;
  double b;
};

union B y =
{
  .b = 12.34
};

指示付きの初期化子と普通の初期化子を混在させることもできます。

int a[] =
{
  [3] = 5,
  1, 2, 3,
};

このように書くと、配列 a の内容は { 0, 0, 0, 5, 1, 2, 3 } になります。
つまり、指示付きの初期化子のあとに普通の初期化子を書くと、直前の要素指示子の次の要素から初期値が設定されます。

指示付きの初期化子を使うと、ちょっと複雑な配列や構造体の値を初期化子として設定することができます。
いったん配列や構造体のオブジェクトを宣言して、あとから各要素に代入することは簡単ですけど、それではオブジェクトをROMに配置したい場合などは不都合が出てきます。

最初慣れるまではちょっと違和感があるかもしれませんね。
けれども、いったん使いこなせるようになれば、この指示付きの初期化子は無くてはならない機能になるはずです。

ところで、この指示付きの初期化子は昔のCでは使うことができませんでした。
使えるようになったのはC99からです。

現在は主立った処理系はC99に対応していると思います(それより新しいC11以降に対応していない処理系はまだたくさんあるようです)。
特別な理由がなければ、C99は積極的に導入していいと思いますよ。

ちなみにこの指示付きの初期化子は、もともとはGCCの独自拡張だったようですね。
GCCの独自拡張はさらに強力で、[3 … 5] のように範囲指定までできます。

もうひとつちなみにですけど、この指示付きの初期化子は長い間C++では使えませんでした。
C++20でようやく導入されたんですけど、Cに比べると制約が多くてかなり不便です。
私はこの指示付きの初期化子を制限なく使いたいために、あえてC++ではなくCを選択することもあるぐらいです。

ちょっと乱雑になってしまいましたが、今回はこのあたりにしておこうと思います。

それではまた!