構造体の全メンバを 0 で埋める | Programming Place Plus C言語編 逆引き

トップページC言語編逆引き

このページの概要

以下は目次です。

目的

定義済みの構造体のすべてのメンバに、0 を入れたいとします。

#include <stdio.h>

struct Data_tag {
    int a;
    double b;
    char c[4];
    struct Data_tag* d;
};

void print_data(const struct Data_tag* data)
{
    printf(".a == %d\n", data->a);
    printf(".b == %lf\n", data->b);
    printf(".c == %s\n", data->c);
    printf(".d == %p\n", data->d);
}

int main(void)
{
    struct Data_tag s;

    // s のすべてのメンバに 0 を入れる

    print_data(&s);
}

実行した結果、次のように出力されることを望むものとします(浮動小数点数の表示桁数や、ヌルポインタの表現は処理系によって異なる可能性があります)。

実行結果:

.a == 0
.b == 0.000000
.c ==
.d == 0000000000000000

なお、構造体変数を定義した時点で、すべてのメンバに 0 を入れておきたいのであれば、次のように書けばよいですし、そうすべきです。

struct Data_tag s = {0};

初期値の個数が不足している場合、残りのメンバは自動的に 0、0.0、ヌルポインタといった値で初期化されることが保証されています(第26章)。

方法①(1つずつ代入する)

各要素に1つ1つ代入していきます。単純ですが、確実に正しい結果が得られる方法です。

#include <stdio.h>

struct Data_tag {
    int a;
    double b;
    char c[4];
    struct Data_tag* d;
};

void print_data(const struct Data_tag* data)
{
    printf(".a == %d\n", data->a);
    printf(".b == %lf\n", data->b);
    printf(".c == %s\n", data->c);
    printf(".d == %p\n", data->d);
}

int main(void)
{
    struct Data_tag s;

    s.a = 0;
    s.b = 0.0;
    s.c[0] = '\0';
    s.d = NULL;

    print_data(&s);
}

実行結果:

.a == 0
.b == 0.000000
.c ==
.d == 0000000000000000

s.d への代入は NULL の代わりに 0 を使っても構いません。

s.c に関して、有効な文字列として扱う分には、s[0] に ‘\0’ が入っていれば問題ありません。s[0]~s[3] のそれぞれに ‘\0’ を入れたいのなら、memset関数 を使うか、for文で1つずつ入れます(逆引き「配列の全要素を 0 で埋める」を参照)。

構造体変数をクリアする処理として関数化しておくのも良いでしょう。

void clear_data(struct Data_tag* data)
{
    data->a = 0;
    data->b = 0.0;
    data->c[0] = '\0';
    data->d = NULL;
}

あとから構造体のメンバの個数を増やしたとき、そのメンバに対して 0 を代入するコードを忘れずに追加しなければならないことに注意が必要です。

【上級】この方法では、構造体メンバの間や、最後のメンバの後ろにあるかもしれないパディング第26章)の部分に 0 は入りません(代入していないのだから当たり前ですが)。

方法②(複合リテラルを使う)

複合リテラル(第26章)を使うと、すべてのメンバにまとめて 0 を代入できます。

方法① よりもこちらの方が簡単ですし、構造体のメンバが追加されても自動対応できる点でも優れていますが、C99規格で追加された機能であるため、古いコンパイラでは対応していません。

#include <stdio.h>

struct Data_tag {
    int a;
    double b;
    char c[4];
    struct Data_tag* d;
};

void print_data(const struct Data_tag* data)
{
    printf(".a == %d\n", data->a);
    printf(".b == %lf\n", data->b);
    printf(".c == %s\n", data->c);
    printf(".d == %p\n", data->d);
}

int main(void)
{
    struct Data_tag s;

    s = (struct Data_tag){0};

    print_data(&s);
}

実行結果:

.a == 0
.b == 0.000000
.c ==
.d == 0000000000000000

初期値は先頭の1つだけ明示すれば、残りはデフォルトの値が使われます。デフォルトの値は、0、0.0、ヌルポインタといったものですから、目的に合っています。

【上級】この方法では、構造体メンバの間や、最後のメンバの後ろにあるかもしれないパディング(第26章)の部分に 0 は入りません。

方法③(memset関数を使う ※正しくない可能性がある)

memset関数を使うと、メモリの範囲を同じ値で埋めることができるため、これを使って、すべてのメンバを 0 で埋める方法が紹介されることがあります。

#include <stdio.h>
#include <string.h>

struct Data_tag {
    int a;
    double b;
    char c[4];
    struct Data_tag* d;
};

void print_data(const struct Data_tag* data)
{
    printf(".a == %d\n", data->a);
    printf(".b == %lf\n", data->b);
    printf(".c == %s\n", data->c);
    printf(".d == %p\n", data->d);
}

int main(void)
{
    struct Data_tag s;

    memset(&s, 0, sizeof(s));

    print_data(&s);
}

実行結果:

.a == 0
.b == 0.000000
.c ==
.d == 0000000000000000

memset関数を使うには、<string.h> のインクルードが必要です。

この方法は、多くの処理系では正しい結果が得られますが、うまくいかない場合もあり得ます。メンバの型が、ポインタ型や、浮動小数点型の配列の場合、「0」で埋めることが必ずしもヌルポインタや 0.0 のことを意味しないためです(第34章)。

このサンプルプログラムでいえば、s.a と s.c については結果が保証できますが、s.b と s.d の結果は保証されません。

【上級】この方法では、構造体メンバの間や、最後のメンバの後ろにあるかもしれないパディング(第26章)の部分も 0 で埋められます。


参考リンク


更新履歴

’2019/9/16 新規作成。



逆引きのトップページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る