C言語:構造体の初期化
C99から、構造体をメンバー単位で初期化できるようになった。
code:sample.c
struct Foo {
int i;
int undef_sample;
double d;
void *p;
};
int main() {
// 初期化子なし
struct Foo undefFoo;
// 初期化子による初期化
struct Foo foo = {
.i = 1,
.d = 3.14,
.p = main,
.s = "Hello"
};
// 初期化子による初期化(すべて0)
struct Foo fooZero = {0};
// memset(&fooZero, 0, sizeof(fooZero)); // ←これは元々不正なやり方
// コンパイラが未初期化警告を出すはず
printf("undefFoo.i=%d\n", undefFoo.i);
printf("undefFoo.undef_sample=%d\n", undefFoo.undef_sample);
printf("undefFoo.d=%g\n", undefFoo.d);
printf("undefFoo.p=%p\n", undefFoo.p);
printf("undefFoo.s=%s\n", undefFoo.s);
printf("foo.i=%d\n", foo.i);
printf("foo.undef_sample=%d\n", foo.undef_sample);
printf("foo.d=%g\n", foo.d);
printf("foo.p=%p\n", foo.p);
printf("foo.s=%s\n", foo.s);
printf("fooZero.i=%d\n", fooZero.i);
printf("fooZero.undef_sample=%d\n", fooZero.undef_sample);
printf("fooZero.d=%g\n", fooZero.d);
printf("fooZero.p=%p\n", fooZero.p);
printf("fooZero.s=%p\n", fooZero.s);
// 複合リテラルによる代入(ゼロクリア)
foo = (struct Foo){0};
printf("foo.i=%d\n", foo.i);
printf("foo.undef_sample=%d\n", foo.undef_sample);
printf("foo.d=%g\n", foo.d);
printf("foo.p=%p\n", foo.p);
printf("foo.s=%p\n", foo.s);
// 複合リテラルによる代入
foo = (struct Foo){
.i = 2,
.d = 2.71,
.p = main,
.s = "Hello"
};
printf("foo.i=%d\n", foo.i);
printf("foo.undef_sample=%d\n", foo.undef_sample);
printf("foo.d=%g\n", foo.d);
printf("foo.p=%p\n", foo.p);
printf("foo.s=%s\n", foo.s);
return EXIT_SUCCESS;
}
指定されないメンバーはゼロにクリアされる。
また、初期化内容として{0}を指定することで、すべてのメンバーをゼロクリア(ポインタはNULL)することができるようになった。
gcc 4.8 では{0}を正しく解釈できない模様。以下のエラーが出る。
警告: 初期化子の周りに中括弧がありません [-Wmissing-braces]
いずれかのメンバーの初期化子を1つ入れると回避できる。
これで「memset で構造体をゼロクリアすると汎用性がない(実装依存)」という問題が解消されるようになった。
ポインタ、浮動小数点数は、メモリをゼロクリアしてもゼロ、NULLになるわけではない。実装依存。なので、構造体のゼロクリアに memset を使うのは本来は不正なやり方で汎用性がない。
(整数のプリミティブ型はメモリのゼロクリアで合致する)
たまたま、多くのアーキテクチャで、IEEE 754 浮動小数点数がメモリがゼロクリアされていると0と同値になることと、数値0がNULLポインタと同値であることから memset でメモリを0埋めすると正しくクリアされた。
初期化を忘れると不定値が入る問題があったが、構造体ではその問題がなくなった。
初期化子を指定しない場合は、構造体全体が不定値となる。
初期化処理をするかしないかはプログラマに一任されていて、不要な初期化処理によるパフォーマンス低下は発生しないようになっている。