構造体と共用体の初期化
構造体型または共用体型のオブジェクトを初期化するときは、初期化子は、空でない、波括弧で囲まれた、コンマ区切りの、メンバの初期化子のリストでなければなりません。
= { expression , ... }
|
(C99未満) | ||||||||
= { designator(オプション) expression , ... }
|
(C99以上) | ||||||||
ただし designator は . member 形式の個々のメンバ指示子または [ index ] 形式の配列指示子の (ホワイトスペースで区切られたまたは隣接した) 並びです。
明示的に初期化されないすべてのメンバは静的記憶域期間を持つオブジェクトと同じ方法で暗黙に初期化されます。
説明
共用体を初期化するときは、初期化子リストはひとつのメンバだけでなければなりません。 これは、指示付き初期化子が使用された場合を除いて、 (C99以上)その共用体の最初のメンバを初期化します。
union { int x; char c[4]; }
u = {1}, // u.x を値 1 を用いてアクティブメンバにします。
u2 = { .c={'\1'} }; // u2.c を値 {'\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 }; // div_t のメンバの順序と違っていても構いません。
|
指示子は、後続の初期化子が、その指示子によって表される構造体メンバを初期化するようにします。 その後の初期化は、指示子によって表されたメンバの次のメンバから、順番通りに継続されます。 struct {int sec,min,hour,day,mon,year;} z
= {.day=31,12,2014,.sec=30,15,17}; // z を {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 = { // 構造体 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 = { // 現在のオブジェクトは ex2 です。 指示子は example のメンバに対するものです。
.in_u.a8[0]=127, 0, 0, 1, .addr=80};
struct example ex3 = {80, .in_u={ // 現在のオブジェクトは ex.in_u に変わります。
127,
.a8[2]=1 // この指示子は in_u のメンバを参照します。
} };
部分オブジェクトが明示的に2回初期化された場合 (指示子が使用されたときに発生する可能性があります) は、リスト内の後の初期化子が使用されます (前の初期化子は評価されないかもしれません)。 struct {int n;} s = {printf("a\n"), // 表示されるかもしれないしされないかもしれません。
.n=printf("b\n")}; // 常に表示されます。
初期化されない部分オブジェクトは暗黙に初期化されますが、部分オブジェクトの暗黙の初期化が同じ部分オブジェクトの明示的な初期化をオーバーライドすることはありません (初期化子内ですでに現れていた場合)。 Run this code #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 を {42, 43, {18, 19} } に初期化します。
int main(void)
{
S l = { 1, // l.i を 1 に初期化します。
.t = x, // l.t を {42, 43, {18, 19} } に初期化します。
.t.l = 41, // l.t を {42, 41, {18, 19} } に変更します。
.t.a[1] = 17 // l.t を {42, 41, {18, 17} } に変更します。
};
printf("l.t.k is %d\n", l.t.k); // .t = x により l.t.k は明示的に 42 に初期化されています。
// .t = x がなければ .t.l = 42 により l.t.k は暗黙に 0 に初期化されます。
}
出力: l.t.k is 42
しかし、初期化子が開き波括弧で始まる場合は、現在のオブジェクトは完全に再初期化され、そのあらゆる部分オブジェクトに対する以前の明示的な初期化は無視されます。 struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // x[0] を { {'a','b','c','\0'}, 1 } に初期化します。
[0].s[0] = 'q' // x[0] を { {'q','b','c','\0'}, 1 } に変更します。
};
struct fred y[ ] = { { { "abc" }, 1 }, // y[0] を { {'a','b','c','\0'}, 1 } に初期化します。
[0] = { // 現在のオブジェクトは y[0] オブジェクト全体になります。
.s[0] = 'q'
} // y[0] を { {'q','\0','\0','\0'}, 0 } で置き換えます。
};
|
(C99以上) |
ノート
初期化子リストは末尾にコンマがあっても構いません。 これは無視されます。
struct {double x,y;} p = {1.0,
2.0, // 末尾にコンマがあっても構いません。
};
C では波括弧で囲まれた初期化子リストは空にできません (C++ では空の波括弧を使用できることに注意してください。 また、 C では構造体を空にできないことにも注意してください)。
struct {int n;} s = {0}; // OK。
struct {int n;} s = {}; // エラー、初期化子リストは空にできません。
struct {} s = {}; // エラー、構造体は空にできませんし、初期化子リストも空にできません。
|
記憶域期間に関わらず、集成体を初期化するとき、初期化子リスト内のすべての式は定数式でなければなりません。 |
(C99未満) |
|
他の初期化と同様に、静的またはスレッドローカル (C11以上)記憶域期間の集成体を初期化するときは、初期化子リスト内のすべての式は定数式でなければなりません。 static struct {char* p} s = {malloc(1)}; // エラー
初期化子内の部分式の評価順序は不定に配列されます。 int n = 1;
struct {int x,y;} p = {n++, n++}; // 未規定ですが well-defined です。
// n は不定な順序で2回インクリメントされます。
// p は {1,2} に初期化されることも {2,1} に初期化されることもあります。
|
(C99以上) |
例
| This section is incomplete Reason: more practical examples, perhaps initialize some socket structs |
#include <stdio.h>
#include <time.h>
int main(void)
{
char buff[70];
// 指示付き初期化子はメンバの順序が未規定な構造体の
// 使用を単純化します。
struct tm my_time = { .tm_year=112, .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
参考文献
- C11 standard (ISO/IEC 9899:2011):
- 6.7.9/12-38 Initialization (p: 140-144)
- C99 standard (ISO/IEC 9899:1999):
- 6.7.8/12-38 Initialization (p: 126-130)
- C89/C90 standard (ISO/IEC 9899:1990):
- 6.5.7 Initialization
関連項目
集成体初期化 の C++リファレンス
|