HOWTO · C

C 言語で配列を 0 に初期化する方法

このチュートリアルでは、C 言語で配列を 0 に初期化するさまざまな方法を紹介します。

このページの内容

int 配列の宣言時にすべての要素をゼロにするには、int values[5] = {0}; とします。Cでは最初の要素が 0 で初期化され、集成体初期化により省略した残りの要素もすべてゼロになります。配列がすでにある場合は、ループで各要素に値を代入します。memset は配列のバイトをゼロで埋められますが、バイト列を埋める方法は、型に応じた初期化の一般的な代替手段ではありません。

宣言時に配列をゼロで初期化する

配列の宣言時にサイズが分かっている場合は、初期化子を使います。次の完全な例では、初期化した5つの値を出力します。

#include <stdio.h>

int main(void) {
    int values[5] = {0};
    for (size_t i = 0; i < sizeof values / sizeof values[0]; ++i) {
        printf("%d%s", values[i], i == 4 ? "\n" : " ");
    }
    return 0;
}
0 0 0 0 0

{0} 初期化子は最初の要素に明示的にゼロを設定し、省略した要素には集成体初期化によってゼロが設定されます。静的記憶域期間またはスレッド記憶域期間を持つオブジェクトは、初期化子を省略してもCが自動的にゼロ初期化します。一方、通常の未初期化のローカル配列は自動記憶域期間を持ち、要素が自動的にゼロになることはありません。読み取る前に初期化子を指定するか、各要素に値を代入してください。

ループで配列をゼロに初期化する

配列をすでに宣言している場合や、実行中に値を決める場合はループが便利です。次の完全な例では、配列の要素数をループの上限に使い、結果を出力します。

#include <stdio.h>

int main(void) {
    int values[5];
    size_t count = sizeof values / sizeof values[0];

    for (size_t i = 0; i < count; ++i) {
        values[i] = 0;
    }
    for (size_t i = 0; i < count; ++i) {
        printf("%d%s", values[i], i + 1 == count ? "\n" : " ");
    }
    return 0;
}
0 0 0 0 0

ここで sizeof が使えるのは、values が同じスコープ内の配列であり、ポインター引数ではないためです。配列を関数に渡す場合は、長さも別に渡してください。関数内では配列引数がポインターに調整されるため、sizeof が返すのはポインターのサイズです。

memset()を使うときの注意点

標準ライブラリ関数 memset は、メモリー領域を同じバイト値で埋めます。<string.h> をインクルードし、次の完全な例のように、現在のスコープ内にある配列全体を対象にするため sizeof values を使います。

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

int main(void) {
    int values[5];
    memset(values, 0, sizeof values);

    for (size_t i = 0; i < sizeof values / sizeof values[0]; ++i) {
        printf("%d%s", values[i], i == 4 ? "\n" : " ");
    }
    return 0;
}
0 0 0 0 0

第3引数はバイト数なので、この配列には sizeof values が適切です。memset は各要素に型付きの値を代入するのではなく、全ビットがゼロの表現を書き込みます。その表現が要素の型と実行環境で適切な場合に限って使ってください。配列を宣言時に意味上のゼロで移植性を保って初期化するには、{0} を使うのが適切です。すべてのC実装で、ゼロのバイト列がヌルポインターや浮動小数点数のゼロを表すとは限りません。

ゼロ以外の値で配列を初期化する

初期化子に必要な値を並べます。たとえば、int values[] = {2, 4, 6}; と書けます。Cは初期化子の数から外側の次元を推論します。多次元配列でこの形式を使う場合、省略できるのは外側の次元だけです。たとえば int grid[][3] = {{1, 2, 3}, {4, 5, 6}}; では、内側の次元を指定する必要があります。

int values[5] = {[0 ... 4] = 7}; のようなGNUの範囲指示子はGCCの拡張であり、移植可能な標準Cの構文ではありません。Cコンパイラー間の移植性が必要な場合は、通常の初期化子リストかループを使ってください。この関数内の複合リテラル (int[]){1, 2, 3} は、名前のない配列をブロックの存続期間で作成します。動的メモリー確保ではなく、一般的な初期化で名前付き配列を宣言する代わりにもなりません。