閉じる

【C言語】配列の初期化はこれでOK! 正しい書き方と注意点

配列の初期化は、C言語の学習初期に必ず通る大切なテーマです。

正しい初期化は、バグの少ない安全なプログラムの第一歩です。

本記事では、整数や浮動小数点、文字配列まで、配列の初期化の基本と注意点を、実行例つきで丁寧に解説します。

初心者の方でも読みながら実験できるよう、具体的なサンプルコードを多数用意しました。

C言語の配列の初期化

宣言と初期化の書き方

配列は型名 配列名[要素数]で宣言し、= { 初期化子, ... }の形で初期化します。

初期化は宣言と同時に1回だけ行えます。

次の表は、よく使う初期化パターンと意味の概要です。

書き方意味と挙動
すべて指定int a[3] = {1, 2, 3};3要素すべてを指定どおりに初期化します。
一部指定(部分初期化)int a[5] = {1, 2};指定しなかった残り要素は0で初期化されます。
サイズ省略int a[] = {10, 20, 30, 40};要素数を省略すると、初期化子の個数から自動で決定されます(この例では4)。
全ゼロint a[5] = {0};最初の要素を0指定すると、残りもすべて0になります。全消去に便利です。
文字列リテラルchar s[] = "abc";末尾に終端文字'\0'が追加され、実際の要素数は4になります。

残りを0埋めする性質は非常に有用です。

例えば設定テーブルなどで、指定していない部分を自動で0にできます。

以下は基本的な宣言と初期化、そして内容の確認です。

C言語
#include <stdio.h>

int main(void) {
    // 完全初期化
    int a1[3] = {1, 2, 3};

    // 部分初期化(残りは0)
    int a2[5] = {1, 2};

    // サイズ省略(初期化子個数が要素数になる)
    int a3[] = {10, 20, 30, 40};

    // 全ゼロ初期化
    int zeros[5] = {0};

    // 内容を表示する(シンプルなループ)
    printf("a1: ");
    for (size_t i = 0; i < 3; ++i) printf("%d ", a1[i]);
    printf("\n");

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

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

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

    return 0;
}
実行結果
a1: 1 2 3 
a2: 1 2 0 0 0 
a3(len=4): 10 20 30 40 
zeros: 0 0 0 0 0

初期化子リストのルール

初期化子は{ 値1, 値2, ... }のようにカンマ区切りで書きます。最後にコンマを置いてもOK(多くのCコンパイラで許容、標準Cでも可能)です。

C言語
int a[3] = {1, 2, 3,};

各初期化子は配列の要素型へ変換されます。例えばdouble配列に整数リテラルを入れると、小数点付きに自動変換されます。

逆にint配列へ小数を入れると小数部分は切り捨てられます。型に合った定数を使うのが安全です。

上級テクニックとして指定初期化子(designated initializer)があります。

C言語
int a[5] = {[2] = 42}; // a[2]だけ42、他は0

初心者のうちは通常の並び順で十分です。

配列サイズの省略

初期化子を与える場合、要素数を省略するとコンパイラが数えてくれます

特に文字列リテラルは終端文字'\0'も含めて数えられる点に注意してください。

C言語
#include <stdio.h>

int main(void) {
    int x[] = {5, 6, 7};           // 要素数3
    char s[] = "abc";              // 'a','b','c','
#include <stdio.h>
int main(void) {
int x[] = {5, 6, 7};           // 要素数3
char s[] = "abc";              // 'a','b','c','\0' の4要素
char t[] = {'a','b','c'};      // 3要素(終端なし)
printf("x: 要素数 = %zu\n", sizeof(x)/sizeof(x[0]));
printf("s: 要素数 = %zu\n", sizeof(s)/sizeof(s[0]));
printf("t: 要素数 = %zu\n", sizeof(t)/sizeof(t[0]));
return 0;
}
' の4要素 char t[] = {'a','b','c'}; // 3要素(終端なし) printf("x: 要素数 = %zu\n", sizeof(x)/sizeof(x[0])); printf("s: 要素数 = %zu\n", sizeof(s)/sizeof(s[0])); printf("t: 要素数 = %zu\n", sizeof(t)/sizeof(t[0])); return 0; }
実行結果
x: 要素数 = 3
s: 要素数 = 4
t: 要素数 = 3

文字列を扱う%sのような関数は終端文字が必要です。

終端の無いt%sで出力してはいけません

ゼロ初期化

全要素を0にしたいときは= {0}が簡単です。

また、静的記憶域(ファイルスコープ変数、またはstaticが付いたローカル配列)は、明示的な初期化がなくても0で初期化されます。

C言語
#include <stdio.h>

// ファイルスコープ(静的記憶域)は暗黙に0初期化
int g[4];

int main(void) {
    static int s[3];   // staticローカルも暗黙に0初期化
    int a[3] = {0};    // 明示的に全ゼロ

    printf("g: ");
    for (size_t i = 0; i < 4; ++i) printf("%d ", g[i]);
    printf("\n");

    printf("s: ");
    for (size_t i = 0; i < 3; ++i) printf("%d ", s[i]);
    printf("\n");

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

    return 0;
}
実行結果
g: 0 0 0 0 
s: 0 0 0 
a: 0 0 0

ブロック内で宣言した非staticなローカル配列は、明示しない限りゼロ初期化されません

これについては後述の注意点で解説します。

整数配列と浮動小数点配列の初期化

完全初期化

整数でも浮動小数点でも、全要素を列挙すれば完全初期化です。

C言語
#include <stdio.h>

int main(void) {
    int ai[4] = {1, -2, 3, 4};
    double ad[3] = {1.0, 2.5, -0.0}; // -0.0は符号付きゼロとして扱われることがあります

    printf("ai: ");
    for (size_t i = 0; i < 4; ++i) printf("%d ", ai[i]);
    printf("\n");

    printf("ad: ");
    for (size_t i = 0; i < 3; ++i) printf("%.2f ", ad[i]);
    printf("\n");
    return 0;
}
実行結果
ai: 1 -2 3 4 
ad: 1.00 2.50 -0.00

部分初期化

初期化子が足りない場合、残りは0で初期化されます。

浮動小数点も0.0になります。

C言語
#include <stdio.h>

int main(void) {
    int ai[5] = {42};          // 先頭のみ42、他は0
    double ad[4] = {1.5, 2.5}; // 残りは0.0

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

    printf("ad: ");
    for (size_t i = 0; i < 4; ++i) printf("%.1f ", ad[i]);
    printf("\n");
    return 0;
}
実行結果
ai: 42 0 0 0 0 
ad: 1.5 2.5 0.0 0.0

定数式サイズと初期化の組み合わせ

配列の要素数に定数式を使うのが基本です。

マクロや列挙定数がよく使われます。

C言語
#include <stdio.h>

#define N 3            // マクロは定数式として使える
enum { M = 4 };        // 列挙定数も定数式

int main(void) {
    int a[N] = {1, 2, 3};
    double b[M] = {0}; // 全ゼロ

    printf("a(len=%zu), b(len=%zu)\n",
           sizeof(a)/sizeof(a[0]), sizeof(b)/sizeof(b[0]));
    return 0;
}
実行結果
a(len=3), b(len=4)
注意

Cではconst int n = 3;nは「定数式」ではありません。

ブロック内でint a[n];のように書くと可変長配列(VLA)になり、VLAは初期化子リストで初期化できません

C言語
// NG例 (多くの処理系でエラー):
// const int n = 3;
// int a[n] = {1, 2, 3}; // VLAは初期化できない

文字配列の初期化

文字列リテラルで初期化

文字列リテラルで初期化すると、末尾に自動で'\0'が入るため、%sで安全に表示できます。

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

int main(void) {
    char s1[] = "hello"; // 実際の要素数は6('h','e','l','l','o','
#include <stdio.h>
#include <string.h>
int main(void) {
char s1[] = "hello"; // 実際の要素数は6('h','e','l','l','o','\0')
printf("s1 = %s\n", s1);
printf("sizeof(s1) = %zu, strlen(s1) = %zu\n",
sizeof(s1), strlen(s1));
return 0;
}
') printf("s1 = %s\n", s1); printf("sizeof(s1) = %zu, strlen(s1) = %zu\n", sizeof(s1), strlen(s1)); return 0; }
実行結果
s1 = hello
sizeof(s1) = 6, strlen(s1) = 5

文字配列と終端文字の注意

終端文字'\0'が入ることを見越して要素数を確保してください。

次は混同しやすい例です。

C言語
// OK: 4文字 + '
// OK: 4文字 + '\0' = 5
char ok1[5] = "hell";
// NG: "hello" は6文字(終端含む)なので要素数5では足りない
// char ng1[5] = "hello"; // コンパイルエラー
// OKだが「文字列」ではない: 終端が無い
char not_str[4] = {'h','e','l','l'};
' = 5 char ok1[5] = "hell"; // NG: "hello" は6文字(終端含む)なので要素数5では足りない // char ng1[5] = "hello"; // コンパイルエラー // OKだが「文字列」ではない: 終端が無い char not_str[4] = {'h','e','l','l'};

終端の無い配列を%sで出力するのは危険です。

必要なら自分で長さを管理し、ループで出力します。

C言語
#include <stdio.h>

int main(void) {
    char not_str[4] = {'h','e','l','l'}; // 終端なし

    // 安全な出力(要素数を使って回す)
    for (size_t i = 0; i < 4; ++i) {
        putchar(not_str[i]);
    }
    putchar('\n');

    return 0;
}
実行結果
hell

1文字ずつの初期化

1文字ずつ並べる方法は、必要なら最後に'\0'を自分で入れるのがポイントです。

C言語
#include <stdio.h>

int main(void) {
    char word[4] = {'A', 'B', 'C', '
#include <stdio.h>
int main(void) {
char word[4] = {'A', 'B', 'C', '\0'}; // ちゃんと終端を入れる
printf("%s\n", word);                 // ABC
return 0;
}
'}; // ちゃんと終端を入れる printf("%s\n", word); // ABC return 0; }
実行結果
ABC

初期化でつまずきやすい注意点

配列全体への代入は不可

Cでは配列同士を=で丸ごと代入できません

次はエラーになります。

C言語
// NG: 配列への代入はできない
// int a[3], b[3] = {1,2,3};
// a = b; // コンパイルエラー

配列の内容をコピーしたい場合はループを使います。

C言語
#include <stdio.h>

int main(void) {
    int src[3] = {1, 2, 3};
    int dst[3]; // 未初期化、これからコピーする

    // 要素ごとのコピー
    for (size_t i = 0; i < 3; ++i) {
        dst[i] = src[i];
    }

    // 結果確認
    printf("dst: ");
    for (size_t i = 0; i < 3; ++i) printf("%d ", dst[i]);
    printf("\n");

    return 0;
}
実行結果
dst: 1 2 3

バイト配列のコピーにはmemcpyも使えますが、まずはループで確実に書けるようにするのがおすすめです。

要素数より多い初期化子はエラー

指定した要素数を超える初期化子はコンパイルエラーです。

C言語
// NG:
// int a[2] = {1, 2, 3}; // 初期化子が多すぎる
// 修正案:
// int a[3] = {1, 2, 3};
// あるいは
// int a[2] = {1, 2};

ローカル配列の未初期化は不定値で危険

ブロック内(関数内)の通常のローカル配列は、明示的に初期化しないと中身は不定です。

読み出すと予測不能な値になります。

C言語
#include <stdio.h>

int main(void) {
    int a[3]; // 未初期化(不定値)
    printf("a: ");
    for (size_t i = 0; i < 3; ++i) {
        // 環境によっては警告が出ます(-Wuninitializedなど)
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}
実行結果
a: 32764 0 4199040  (例。実行のたびに異なる可能性があります)

未初期化のまま使わないこと

必要に応じて={0}やループで初期化しましょう。

全要素を同じ値にするにはループが必要

全要素を任意の値で揃えるにはループで代入します。

ゼロにするだけなら={0}が最短です。

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

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

    // 1) ゼロで初期化(宣言時)
    int z[6] = {0};

    // 2) 任意の値(ここでは7)で埋める: ループが正攻法
    for (size_t i = 0; i < 6; ++i) {
        a[i] = 7;
    }

    // 3) memsetは「0で埋める」には使えるが、整数の任意値には不向き
    int m[6];
    memset(m, 0, sizeof m); // 0ならOK(全ビット0が整数0に対応)

    // 出力
    printf("z: ");
    for (size_t i = 0; i < 6; ++i) printf("%d ", z[i]);
    printf("\n");

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

    printf("m(memset 0): ");
    for (size_t i = 0; i < 6; ++i) printf("%d ", m[i]);
    printf("\n");

    return 0;
}
実行結果
z: 0 0 0 0 0 0 
a(=7): 7 7 7 7 7 7 
m(memset 0): 0 0 0 0 0 0
注意

memset(a, 7, sizeof a);のように非ゼロで埋めると「各バイトを0x07で埋める」だけなので、整数の7とは無関係のビットパターンになり、意図した結果にはなりません。

整数や浮動小数点を任意値で揃える場合は必ずループで代入しましょう。

まとめ

配列の初期化はシンプルに見えて、終端文字や要素数、定数式の扱いなど細かなルールを正しく理解することが大切です。

特に次のポイントを押さえておけば、初期化で迷うことはほとんどありません。

  • 部分初期化では残りが0で埋まるため、必要なところだけ書けばよい。全ゼロは={0}でOK。
  • サイズ省略は便利。ただし文字列リテラルでは'\0'も含まれる点に注意。
  • ローカル配列の未初期化は厳禁。使う前に初期化する。
  • 配列に代入はできないので、コピーはループで行う。
  • 定数式サイズを使う(マクロや列挙定数)。VLAは初期化子リストで初期化できない。

これらを守れば、配列の初期化は怖くありません。

サンプルを手元で動かしながら、「いつ0になるか」「サイズがどう決まるか」「終端が入るか」を体感して、自信をつけていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

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

URLをコピーしました!