閉じる

【C言語】配列の初期化入門|宣言から一括・0埋めまでサンプル付き

プログラムを書くときに「配列の初期化」は必ず登場しますが、意外と書き方のバリエーションや言語仕様を正しく理解できていないことが多いです。

本記事では、C言語の配列初期化について、宣言と同時に行う方法から、一括初期化・0埋め・構造体配列まで、代表的なパターンをサンプルコードつきで整理して解説します。

C言語の配列初期化とは

配列初期化の基本とメリット

C言語の配列初期化とは、配列を宣言すると同時に、その各要素に最初の値を設定することです。

配列は「連続したメモリ領域に同じ型の要素が並んだもの」なので、使い始める前にどのような値が入っているかを明確にしておくことが重要です。

初期化を行わない場合、ローカル変数の配列には不定値(ゴミ)が入っているため、そのまま使うと予期しない挙動やバグにつながります。

一方で、初期化を書いておけば、配列の初期状態をコード上で一目で確認でき、デバッグや保守もしやすくなります。

初期化をしておくメリットを整理すると、次のようになります。

配列初期化の主なメリットとしては、配列の値が常に定義済みの状態からスタートすること、コードを読む人にとって配列の用途や初期状態が直感的に理解しやすくなること、そしてテストやデバッグ時に再現性の高い動作が得られることがあります。

このように、初期化は単なる書式の問題ではなく、品質の高いコードを書くうえでの基本的な作法といえます。

宣言と同時に初期化する書き方

配列の初期化は、もっとも一般的には宣言と同時に行います。

基本形は次の通りです。

C言語
型名 配列名[要素数] = { 初期値1, 初期値2, ... };

基本的な例

C言語
#include <stdio.h>

int main(void) {
    // int型の配列を宣言と同時に初期化
    int numbers[5] = {1, 2, 3, 4, 5};

    // char型の配列(文字列として利用)を宣言と同時に初期化
    char message[6] = "Hello";  // 終端の'\0'を含めて6要素

    // 配列の中身を表示して確認
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }

    printf("message: %s\n", message);

    return 0;
}
実行結果
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
message: Hello

このように宣言と同時に初期化しておくと、配列のサイズと初期値の関係がコード上で明確になり、後から見返したときにも理解しやすくなります。

一括初期化の書き方と注意点

配列を一括初期化する基本構文

配列の初期化には、波かっこ{ }を使って一括で値を並べる書き方を用います。

これを「一括初期化」と呼ぶことがあります。

C言語
型名 配列名[要素数] = { 初期値1, 初期値2, 初期値3, ... };

一括初期化の具体例

C言語
#include <stdio.h>

int main(void) {
    // int型配列を一括初期化
    int scores[4] = {80, 90, 75, 100};

    // double型配列を一括初期化
    double temperatures[3] = {36.5, 37.0, 36.8};

    // 結果を確認
    for (int i = 0; i < 4; i++) {
        printf("scores[%d] = %d\n", i, scores[i]);
    }
    for (int i = 0; i < 3; i++) {
        printf("temperatures[%d] = %.1f\n", i, temperatures[i]);
    }

    return 0;
}
実行結果
scores[0] = 80
scores[1] = 90
scores[2] = 75
scores[3] = 100
temperatures[0] = 36.5
temperatures[1] = 37.0
temperatures[2] = 36.8

要素数と初期化子の数の関係

配列の初期化では、配列の要素数と波かっこ内の初期値の個数(初期化子の数)の関係が重要です。

C言語では、次のようなルールがあります。

1. 要素数と初期化子の数が一致する場合

C言語
int a[3] = {1, 2, 3};  // 要素数3、初期化子も3つ

この場合は素直に、a[0]に1, a[1]に2, a[2]に3が代入されます。

2. 要素数のほうが多い場合(足りない分は0)

C言語
#include <stdio.h>

int main(void) {
    // 要素数5に対し、初期化子は3つだけ
    int a[5] = {1, 2, 3};  // 残り2要素は自動的に0で初期化される

    for (int i = 0; i < 5; i++) {
        printf("a[%d] = %d\n", i, a[i]);
    }

    return 0;
}
実行結果
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 0
a[4] = 0

初期化リストが途中で終わった場合、残りは0で埋められるという仕様は、後述する「部分的な初期化」とも関係します。

3. 要素数を省略した場合(初期化子の数から決まる)

Cでは配列宣言時に要素数を省略し、初期化子の数から自動的に要素数を決めることもできます。

C言語
#include <stdio.h>

int main(void) {
    // 要素数を省略すると、初期化子の数(4つ)から自動的に配列サイズが決まる
    int a[] = {10, 20, 30, 40};

    int length = sizeof(a) / sizeof(a[0]);
    printf("配列の要素数は %d です\n", length);

    return 0;
}
実行結果
配列の要素数は 4 です

この書き方を使うと、初期値の数を変えたときに要素数の指定を変更し忘れるといったヒューマンエラーを減らすことができます。

部分的な初期化と残り要素の扱い

部分的な初期化とは、配列の一部の要素にだけ明示的な初期値を与え、残りを暗黙的に0で埋める書き方のことです。

シンプルな部分初期化

C言語
#include <stdio.h>

int main(void) {
    // 部分的な初期化。残りはすべて0になる
    int a[5] = {1, 2};  // a[2], a[3], a[4] は 0

    for (int i = 0; i < 5; i++) {
        printf("a[%d] = %d\n", i, a[i]);
    }

    return 0;
}
実行結果
a[0] = 1
a[1] = 2
a[2] = 0
a[3] = 0
a[4] = 0

配列の残りを0にしたい場合に、この性質をうまく利用すると読みやすくエラーの少ないコードが書けます

指定初期化子(配列インデックスを指定して初期化する)

C99以降では[インデックス] = 値という形式の「指定初期化子」も利用できます。

これを使うと特定の要素だけをピンポイントで初期化できます。

C言語
#include <stdio.h>

int main(void) {
    // 指定初期化子を使った部分初期化
    int a[10] = {
        [0] = 1,
        [5] = 100
        // 他の要素は自動的に0で初期化される
    };

    for (int i = 0; i < 10; i++) {
        printf("a[%d] = %d\n", i, a[i]);
    }

    return 0;
}
実行結果
a[0] = 1
a[1] = 0
a[2] = 0
a[3] = 0
a[4] = 0
a[5] = 100
a[6] = 0
a[7] = 0
a[8] = 0
a[9] = 0

指定初期化子はC99以降の機能なので、古いコンパイラでは使えない場合があります。

利用する際は自分のコンパイラとオプションを確認しておくと安心です。

0埋め(ゼロ初期化)のテクニック

C言語で配列を0埋めする3つの方法

配列をすべて0で初期化する(ゼロ初期化)場面は非常に多くあります。

代表的な方法は次の3つです。

1つずつ具体的に見ていきます。

方法1: 宣言時に{0}で一括ゼロ初期化

もっともシンプルで安全なのが、宣言時の{0}による初期化です。

C言語
#include <stdio.h>

int main(void) {
    // すべての要素が0で初期化される
    int a[5] = {0};    // a[0] に 0 を代入、残りも自動的に 0
    char b[10] = {0};  // すべて '\0'

    for (int i = 0; i < 5; i++) {
        printf("a[%d] = %d\n", i, a[i]);
    }

    return 0;
}
実行結果
a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 0
a[4] = 0

初期化リストに0を1つだけ書くと、残りは自動的に0で埋められるという仕様を利用しています。

方法2: 静的ストレージ期間の配列は自動的に0

Cでは、グローバル変数関数内のstatic変数など、静的ストレージ期間を持つオブジェクトは明示的に初期化しなくても自動的に0で初期化されます。

C言語
#include <stdio.h>

// グローバル配列は自動的に0で初期化される
int global_array[5];

void func(void) {
    // 関数内static配列も自動的に0で初期化
    static int static_array[5];

    for (int i = 0; i < 5; i++) {
        printf("static_array[%d] = %d\n", i, static_array[i]);
    }
}

int main(void) {
    for (int i = 0; i < 5; i++) {
        printf("global_array[%d] = %d\n", i, global_array[i]);
    }

    func();
    return 0;
}
実行結果
global_array[0] = 0
global_array[1] = 0
global_array[2] = 0
global_array[3] = 0
global_array[4] = 0
static_array[0] = 0
static_array[1] = 0
static_array[2] = 0
static_array[3] = 0
static_array[4] = 0

この性質により、大きな配列をグローバルに置くと自動的にゼロ初期化されるため、初期化コードを書かずに済む場合があります。

ただし、ローカル変数の配列(自動変数)にはこの自動ゼロ初期化は適用されません

方法3: 実行時にmemsetで0埋めする

配列を宣言後に0でクリアしたい場合は、memsetを利用します。

C言語
#include <stdio.h>
#include <string.h>  // memsetの宣言がある

int main(void) {
    int a[5];

    // いったん適当な値を入れる
    for (int i = 0; i < 5; i++) {
        a[i] = i + 1;
    }

    // 配列aの全バイトを0で埋める
    memset(a, 0, sizeof(a));

    for (int i = 0; i < 5; i++) {
        printf("a[%d] = %d\n", i, a[i]);
    }

    return 0;
}
実行結果
a[0] = 0
a[1] = 0
a[2] = 0
a[3] = 0
a[4] = 0

実行途中で配列をリセットしたい場合などには、このmemsetによる0埋めがよく利用されます。

静的変数・グローバル配列のゼロ初期化

さきほど触れた通り、静的ストレージ期間を持つオブジェクトは暗黙に0初期化されます。

ここでは、その挙動を少し詳しく整理します。

自動で0になる対象

次のような配列は、初期化を書かなくてもすべて0になります。

C言語
// ファイルスコープの配列(グローバル変数)
int g1[100];

// static指定されたファイルスコープの配列
static int g2[100];

void func(void) {
    // 関数内static配列
    static int s1[100];
}

これらはプログラム起動時に一度だけ0に初期化されます。

関数内staticについては、関数が何度呼ばれても最初の1回だけ初期化が行われ、その後は値が保持され続けます。

自動変数(ローカル変数)との違い

一方で、関数内で宣言される通常の配列(自動変数)は自動的には0になりません

C言語
#include <stdio.h>

void func(void) {
    int local[5];  // 自動変数の配列。中身は不定(ゴミ)値。

    for (int i = 0; i < 5; i++) {
        printf("local[%d] = %d\n", i, local[i]);
    }
}

int main(void) {
    func();
    return 0;
}
実行結果
local[0] = 4196512
local[1] = 0
local[2] = 32767
local[3] = 0
local[4] = 4196544

出力内容は環境によって異なり、何が出るかは保証されません

ローカル配列を使う場合は、{0}などで明示的に初期化するか、memsetを利用して確実に0埋めしておく必要があります。

memsetでの0埋めと注意点

memsetは便利ですが、使い方を誤るとバグの温床にもなります。

ここでは、0埋めの正しい使い方と注意点を整理します。

memsetの基本形

C言語
void *memset(void *s, int c, size_t n);
  • s: 埋めたいメモリ領域の先頭アドレス
  • c: 書き込む値(下位1バイトが使われる)
  • n: 書き込むバイト数

0埋めする場合はc0を指定し、nにはsizeofで配列全体のサイズ(バイト数)を指定するのが基本形です。

C言語
memset(a, 0, sizeof(a));

整数配列・構造体への0埋めはOK

整数型・浮動小数点型・ポインタ型・構造体・配列など、Cで一般的に扱う多くの型に対して、0埋めは意味を持ちます。

特に整数やポインタでは、ビットパターンがすべて0になることは数値0NULLポインタに対応するため、安全に利用できます。

C言語
#include <stdio.h>
#include <string.h>

struct Point {
    int x;
    int y;
};

int main(void) {
    struct Point p;
    int array[3];

    // 構造体と配列をすべて0で埋める
    memset(&p, 0, sizeof(p));
    memset(array, 0, sizeof(array));

    printf("p.x = %d, p.y = %d\n", p.x, p.y);
    for (int i = 0; i < 3; i++) {
        printf("array[%d] = %d\n", i, array[i]);
    }

    return 0;
}
実行結果
p.x = 0, p.y = 0
array[0] = 0
array[1] = 0
array[2] = 0

memset使用時の注意点

特に注意したいポイントは次の通りです。

  • 書き込むバイト数nの指定ミス
    配列の要素数ではなくバイト数を指定する必要があります。sizeofを使うと安全です。
  • 0以外で整数以外の型を埋めるのは要注意
    memset(a, 1, sizeof(a));などは、各バイトに0x01を書き込みますが、これはintとしての数値1とは異なるビットパターンになり得ます。整数配列に0以外を入れたいときはループで代入したほうが安全です。
  • Cの規格上は浮動小数点型のビットパターン0が「0.0」でない可能性が理屈上は存在
    実際のほとんどの環境(IEEE 754)ではmemsetで0埋めしても0.0になりますが、標準の規格上は実装依存です。ポータビリティを極限まで重視する場合には注意が必要です。

配列初期化のサンプルコード集

int配列の初期化サンプル

ここでは、int型配列の基本的な初期化パターンをまとめて確認します。

代表的なパターンまとめ

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    // 1. 宣言と同時に完全初期化
    int a[5] = {1, 2, 3, 4, 5};

    // 2. 部分的な初期化(残りは0)
    int b[5] = {10, 20}; // b[2], b[3], b[4] は 0

    // 3. {0} による0埋め
    int c[5] = {0}; // すべて 0

    // 4. 要素数を省略して初期化
    int d[] = {7, 8, 9}; // 要素数は3になる

    // 5. memsetを使った0埋め
    int e[5] = {1, 2, 3, 4, 5};
    memset(e, 0, sizeof(e));

    printf("a: ");
    for (int i = 0; i < 5; i++) printf("%d ", a[i]);
    printf("\n");

    printf("b: ");
    for (int i = 0; i < 5; i++) printf("%d ", b[i]);
    printf("\n");

    printf("c: ");
    for (int i = 0; i < 5; i++) printf("%d ", c[i]);
    printf("\n");

    printf("d: ");
    for (int i = 0; i < (int)(sizeof(d) / sizeof(d[0])); i++)
        printf("%d ", d[i]);
    printf("\n");

    printf("e: ");
    for (int i = 0; i < 5; i++) printf("%d ", e[i]);
    printf("\n");

    return 0;
}
実行結果
a: 1 2 3 4 5 
b: 10 20 0 0 0 
c: 0 0 0 0 0 
d: 7 8 9 
e: 0 0 0 0 0

このように、用途に応じて初期化パターンを組み合わせることで、意図が明確なコードになります。

char配列(文字列)の初期化サンプル

文字列を扱うchar配列の初期化には、文字列リテラルを使う方法と、文字列の各文字を並べる方法があります。

文字列リテラルで初期化する

C言語
#include <stdio.h>

int main(void) {
    // 終端の '\0' を含めて6バイト必要
    char s1[6] = "Hello";

    // 要素数を省略すると、自動的にサイズが決まる(この場合6)
    char s2[] = "World";

    printf("s1 = %s\n", s1);
    printf("s2 = %s\n", s2);

    // 配列のサイズと文字列長を確認
    printf("sizeof(s1) = %zu\n", sizeof(s1));
    printf("sizeof(s2) = %zu\n", sizeof(s2));

    return 0;
}
実行結果
s1 = Hello
s2 = World
sizeof(s1) = 6
sizeof(s2) = 6

文字列リテラルで初期化した場合、末尾には自動的に'\0'が追加されます。

そのため、配列サイズを自分で決めるときは文字数 + 1を確保する必要があります。

文字を並べて初期化する

C言語
#include <stdio.h>

int main(void) {
    // 文字を1つずつ並べて初期化(終端に '\0' を忘れないこと)
    char s[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

    printf("s = %s\n", s);

    return 0;
}
実行結果
s = Hello

文字配列をmemsetで0埋めしてから、一部だけ文字を入れるパターンもよく使われます。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char buf[100];

    // バッファ全体を0で埋める(安全な文字列バッファの初期化)
    memset(buf, 0, sizeof(buf));

    // 先頭に簡単なメッセージを入れる
    buf[0] = 'O';
    buf[1] = 'K';

    printf("buf = \"%s\"\n", buf);

    return 0;
}
実行結果
buf = "OK"

文字列は必ず'\0'で終端されている必要があるため、0埋めと相性が良いことを覚えておくと便利です。

構造体配列の初期化サンプル

最後に、構造体配列の初期化を見てみます。

メンバが複数ある構造体でも、配列と同じく初期化リストを使って分かりやすく初期値を与えられます。

基本的な構造体配列の初期化

C言語
#include <stdio.h>

// 人物情報を表す構造体
struct Person {
    char name[16];  // 名前(最大15文字 + 終端)
    int age;        // 年齢
};

int main(void) {
    // 構造体配列を宣言と同時に初期化
    struct Person people[3] = {
        {"Alice", 20},
        {"Bob",   25},
        {"Carol", 30}
    };

    // 情報を表示
    for (int i = 0; i < 3; i++) {
        printf("people[%d]: name=%s, age=%d\n",
               i, people[i].name, people[i].age);
    }

    return 0;
}
実行結果
people[0]: name=Alice, age=20
people[1]: name=Bob, age=25
people[2]: name=Carol, age=30

このように、構造体1つ分を波かっこで囲み、それをさらに配列の波かっこの中に並べる形になります。

構造体配列の部分初期化と0埋め

構造体配列でも、{0}や部分初期化を利用できます。

C言語
#include <stdio.h>
#include <string.h>

struct Point {
    int x;
    int y;
};

int main(void) {
    // 1. 構造体配列をすべて0で初期化
    struct Point points1[3] = {0};  // x, y すべて 0

    // 2. 一部だけ初期化し、残りを0にする
    struct Point points2[3] = {
        {1, 2},    // points2[0]
        {3, 4}     // points2[1]
        // points2[2] は {0, 0} になる
    };

    // 3. 指定初期化子を使う(C99以降)
    struct Point points3[3] = {
        [0] = {.x = 10, .y = 20},  // メンバ名を使った初期化
        [2] = {.x = 30}            // y は 0 になる
    };

    // 結果確認
    printf("points1:\n");
    for (int i = 0; i < 3; i++) {
        printf("  [%d] = (%d, %d)\n", i, points1[i].x, points1[i].y);
    }

    printf("points2:\n");
    for (int i = 0; i < 3; i++) {
        printf("  [%d] = (%d, %d)\n", i, points2[i].x, points2[i].y);
    }

    printf("points3:\n");
    for (int i = 0; i < 3; i++) {
        printf("  [%d] = (%d, %d)\n", i, points3[i].x, points3[i].y);
    }

    return 0;
}
実行結果
points1:
  [0] = (0, 0)
  [1] = (0, 0)
  [2] = (0, 0)
points2:
  [0] = (1, 2)
  [1] = (3, 4)
  [2] = (0, 0)
points3:
  [0] = (10, 20)
  [1] = (0, 0)
  [2] = (30, 0)

構造体配列は実際の開発で頻繁に登場するため、{0}によるゼロ初期化部分初期化のパターンを押さえておくと、設定テーブル・マスターデータ・座標一覧などを整然と扱えるようになります。

まとめ

C言語の配列初期化は一見シンプルですが、一括初期化・部分初期化・ゼロ初期化・構造体配列の初期化など、実務で使いこなすにはいくつかのパターンを理解しておく必要があります。

特に{0}による0埋め要素数省略は、書き間違いを防ぎ、コードの意図を明確にするうえで有効です。

配列は多くのプログラムの土台となる基本要素なので、本記事のサンプルを手元で実行しながら、確実に自分のものにしていくと良いです。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!