名前空間
変種
操作

構造体と共用体の初期化

From cppreference.com
< c‎ | language

構造体型または共用体型のオブジェクトを初期化する際、初期化子は、メンバの初期化子の、波括弧で囲まれ、コンマで区切られたリストでなければなりません。これは、空でない、(C23まで)という条件があります。

= { , ... } (1) (C99まで)
= { 指定子(省略可能) , ... } (2) (C99以降)
= { } (3) (C23以降)

ここで、指定子とは、個々のメンバ指定子(whitespaceで区切られるか、隣接している)のシーケンスで、形式は . メンバおよび配列指定子の形式は [ インデックス ] です。

明示的に初期化されないすべてのメンバは、空初期化されます。

目次

[編集] 説明

共用体を初期化する場合、初期化子リストは1つのメンバのみを持つ必要があり、それは共用体の最初のメンバを初期化します(指定子付き初期化子が使用されない限り(C99以降))。

union { int x; char c[4]; }
  u = {1},           // makes u.x active with value 1
 u2 = { .c={'\1'} }; // makes u2.c active with value {'\1','\0','\0','\0'}

構造体を初期化する場合、リストの最初の初期化子は、宣言された最初のメンバを初期化します(指定子が指定されない場合(C99以降))。指定子なしのすべての後続の初期化子は、指定子なしの場合(C99以降)前の式によって初期化されたメンバの後に宣言された構造体メンバを初期化します。

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 };      // order of elements in div_t may vary

指定子は、後続の初期化子が指定子によって説明される構造体メンバを初期化させます。初期化は、指定子によって説明された要素の後に宣言された次の要素から始まり、宣言の順序で前方へと続行します。

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 = { // start of initializer list for struct example
                     { // start of initializer list for ex.addr
                        80 // initialized struct's only member
                     }, // end of initializer list for ex.addr
                     { // start of initializer-list for ex.in_u
                        {127,0,0,1} // initializes first element of the union
                     } };

ネストされた初期化子が波括弧で始まらない場合、リストから十分な初期化子のみが取得され、メンバ配列、構造体、または共用体の要素またはメンバに対応します。残りの初期化子は、次の構造体メンバの初期化のために残されます。

struct example ex = {80, 127, 0, 0, 1}; // 80 initializes ex.addr.port
                                        // 127 initializes ex.in_u.a8[0]
                                        // 0 initializes ex.in_u.a8[1]
                                        // 0 initializes ex.in_u.a8[2]
                                        // 1 initializes 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, // trailing comma OK
                          };

Cでは、波括弧で囲まれた初期化子リストは空にできません(C++は空リストを許可し、またCの構造体は空にできないことに注意してください)。

(C23まで)

初期化子リストは、C++と同様にCでも空にすることができます。

(C23以降)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // Error until C23: initializer-list cannot be empty
                        // OK since C23: s.n is initialized to 0
struct {} s = {}; // Error: struct cannot be empty

任意のストレージ期間の集約を初期化する場合、初期化子リストのすべての式は定数式でなければなりません。

(C99まで)

他のすべての初期化と同様に、静的(またはスレッドローカル(C11以降))なストレージ期間の集約を初期化する場合、初期化子リストのすべての式は定数式でなければなりません。

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

任意の初期化子内のサブ式の評価順序は、不定にシーケンス化されています(ただし、C++11以降のC++ではそうではありません)。

int n = 1;
struct {int x,y;} p = {n++, n++}; // unspecified, but well-defined behavior:
                                  // n is incremented twice in arbitrary order
                                  // p equal {1,2} and {2,1} are both valid
(C99以降)

[編集]

#include <stdio.h>
#include <time.h>
 
int main(void)
{
    char buff[70];
    // designated initializers simplify the use of structs whose
    // order of members is unspecified
    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 Initialization

[編集] 関連項目

C++ドキュメント集約初期化について)
English 日本語 中文(简体) 中文(繁體)