Namespaces
Variants

Struct and union initialization

From cppreference.net

初期化 時に struct または union 型のオブジェクトを初期化する場合、初期化子はメンバーに対する 空でない (C23以前) 中括弧で囲まれたカンマ区切りの初期化子リストでなければなりません:

= { , ... } (1) (C99まで)
= { 指示子 (オプション) , ... } (2) (C99以降)
= { } (3) (C23以降)

ここで designator は、以下の形式の個々のメンバー指定子のシーケンス(空白区切りまたは隣接)です: . member および array designators の形式: [ index ]

明示的に初期化されないすべてのメンバーは empty-initialized されます。

目次

説明

union を初期化する際、初期化子リストは単一のメンバーを持たなければならず、それはunionの最初のメンバーを初期化します 指示付き初期化子が使用される場合を除く (C99以降)

union { int x; char c[4]; }
  u = {1},           // u.xをアクティブにして値1を設定
 u2 = { .c={'\1'} }; // u2.cをアクティブにして値{'\1','\0','\0','\0'}を設定

struct の初期化時には、初期化子リストの最初の初期化子は最初に宣言されたメンバを初期化します (指示子が指定されている場合を除く) (C99以降) 。その後続くすべての初期化子は 指示子なしの場合 (C99以降) 、前の式で初期化されたメンバの後に宣言されたstructメンバを初期化します。

struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0
div_t answer = {.quot = 2, .rem = -1 };      // div_t内の要素の順序は異なる場合があります

指示子は、その後の初期化子が指示子によって記述された構造体メンバを初期化するようにします。初期化はその後、指示子によって記述された要素の後に宣言された次の要素から始まり、宣言順に前方へ続きます。

struct {int sec,min,hour,day,mon,year;} z
   = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014}
(C99以降)

メンバー数よりも多くの初期化子を指定するとエラーになります。

ネストされた初期化

構造体または共用体のメンバが配列、構造体、または共用体である場合、波括弧で囲まれた初期化子リスト内の対応する初期化子は、それらのメンバに対して有効な任意の初期化子です。ただし、以下のように波括弧を省略することができます:

入れ子初期化子が開きブレースで始まる場合、対応するメンバオブジェクトはその閉じブレースまでの全体の入れ子初期化子によって初期化されます。各左開きブレースは新たな 現在オブジェクト を確立します。現在オブジェクトのメンバは自然順序で初期化されます (指示子が使用されない限り) (C99以降) :配列要素は添字順、構造体メンバは宣言順、共用体の最初に宣言されたメンバのみ。閉じブレースによって明示的に初期化されない現在オブジェクト内の部分オブジェクトは 空初期化 されます。

struct example {
    struct addr_t {
       uint32_t port;
    } addr;
    union {
       uint8_t a8[4];
       uint16_t a16[2];
    } in_u;
};
struct example ex = { // struct exampleの初期化子リストの開始
                     { // ex.addrの初期化子リストの開始
                        80 // 構造体の唯一のメンバを初期化
                     }, // ex.addrの初期化子リストの終了
                     { // ex.in_uの初期化子リストの開始
                        {127,0,0,1} // 共用体の最初の要素を初期化
                     } };

入れ子になった初期化子が開きブレースで始まらない場合、メンバ配列、構造体、または共用体の要素やメンバを構成するのに必要な分だけ初期化子リストから取得され、残りの初期化子は次の構造体メンバの初期化に残されます:

struct example ex = {80, 127, 0, 0, 1}; // 80 は ex.addr.port を初期化
                                        // 127 は ex.in_u.a8[0] を初期化
                                        // 0 は ex.in_u.a8[1] を初期化
                                        // 0 は ex.in_u.a8[2] を初期化
                                        // 1 は ex.in_u.a8[3] を初期化

指示子がネストされている場合、メンバーの指示子は外側の構造体/共用体/配列の指示子に続きます。任意のネストされた括弧付き初期化子リスト内では、最も外側の指示子は 現在のオブジェクト を参照し、 現在のオブジェクト 内で初期化される部分オブジェクトのみを選択します。

struct example ex2 = { // current object is ex2, designators are for members of example
                       .in_u.a8[0]=127, 0, 0, 1, .addr=80}; 
struct example ex3 = {80, .in_u={ // changes current object to the union ex.in_u
                           127,
                           .a8[2]=1 // this designator refers to the member of in_u
                      } };

部分オブジェクトが明示的に2回初期化される場合(指示子が使用されているときに発生する可能性があります)、リスト内で後に現れる初期化子が使用されます(前の初期化子は評価されない可能性があります):

struct {int n;} s = {printf("a\n"), // this may be printed or skipped
                     .n=printf("b\n")}; // always printed

初期化されていない部分オブジェクトは暗黙的に初期化されますが、同じ部分オブジェクトの明示的な初期化が初期化子リストの前の方に現れた場合、部分オブジェクトの暗黙的な初期化はそれを上書きしません(正しい出力を確認するにはclangを選択してください):

#include <stdio.h>
typedef struct { int k; int l; int a[2]; } T;
typedef struct { int i;  T t; } S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 };
 // x initialized to {42, 43, {18, 19} }
int main(void)
{
    S l = { 1,          // initializes l.i to 1
           .t = x,      // initializes l.t to {42, 43, {18, 19} }
           .t.l = 41,   // changes l.t to {42, 41, {18, 19} }
           .t.a[1] = 17 // changes l.t to {42, 41, {18, 17} }
          };
    printf("l.t.k is %d\n", l.t.k); // .t = x sets l.t.k to 42 explicitly
                                    // .t.l = 41 would zero out l.t.k implicitly
}

出力:

l.t.k is 42

ただし、初期化子が左開きブレースで始まる場合、その 現在のオブジェクト は完全に再初期化され、その部分オブジェクトに対する以前の明示的な初期化子はすべて無視されます:

struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // inits x[0] to { {'a','b','c','\0'}, 1 }
                      [0].s[0] = 'q'   // changes x[0] to { {'q','b','c','\0'}, 1 }
                   };
struct fred y[ ] = { { { "abc" }, 1 }, // inits y[0] to { {'a','b','c','\0'}, 1 }
                     [0] = { // current object is now the entire y[0] object
                             .s[0] = 'q' 
                            } // replaces y[0] with { {'q','\0','\0','\0'}, 0 }
                    };
(C99以降)

注記

初期化子リストには末尾にカンマを付けることができ、これは無視されます。

struct {double x,y;} p = {1.0,
                          2.0, // 末尾のカンマは許可されます
                          };

C言語では、初期化子の波括弧リストを空にすることはできません(C++では空リストが許可されており、またCの struct は空にできないことに注意):

(until C23)

C++と同様に、C言語でも初期化子リストを空にすることができます:

(since C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // C23までエラー: 初期化子リストを空にすることはできません
                        // C23以降OK: s.nは0で初期化されます
struct {} s = {}; // エラー: 構造体を空にすることはできません

初期化リスト内のすべての式は、任意のストレージ期間の集成体を初期化する際に constant expression でなければなりません。

(C99まで)

他のすべての initialization と同様に、初期化リスト内のすべての式は、静的 またはスレッドローカル (C11以降) storage duration の集成体を初期化する際に constant expression でなければなりません:

static struct {char* p} s = {malloc(1)}; // error

任意の初期化子における部分式の order of evaluation は不定シーケンスです(ただしC++11以降のC++では異なります):

int n = 1;
struct {int x,y;} p = {n++, n++}; // 未規定だが、明確に定義された動作:
                                  // nは任意の順序で2回インクリメントされる
                                  // pが{1,2}と{2,1}のどちらでも有効
(C99以降)

#include <stdio.h>
#include <time.h>
int main(void)
{
    char buff[70];
    // 指定初期化子はメンバーの順序が不定な構造体の使用を簡素化する
    struct tm my_time = { .tm_year=2012-1900, .tm_mon=9, .tm_mday=9,
                          .tm_hour=8, .tm_min=10, .tm_sec=20 };
    strftime(buff, sizeof buff, "%A %c", &my_time);
    puts(buff);
}

出力例:

Sunday Sun Oct  9 08:10:20 2012

参考文献

  • C17規格 (ISO/IEC 9899:2018):
  • 6.7.9/12-39 初期化 (p: 101-105)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.7.9/12-38 初期化 (p: 140-144)
  • C99規格 (ISO/IEC 9899:1999):
  • 6.7.8/12-38 初期化 (p: 126-130)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 6.5.7 初期化

関連項目

C++ documentation for Aggregate initialization
日本語訳:
C++ documentation Aggregate initialization
変更点: - "for" → "の"(C++ documentation for Aggregate initialization → Aggregate initializationのC++ documentation) - HTMLタグ、属性、C++用語(Aggregate initialization)は翻訳せず保持 - 元のフォーマットを完全に維持