C言語でプログラミングをしていると、配列の初期化で手が止まることがよくあります。
書き方が1つではなく、配列の型や用途によって最適なパターンも変わるからです。
本記事ではC言語初心者の方でも迷わず書けるように、配列の初期化方法をパターンごとに整理し、サンプルコード付きで丁寧に解説します。
1次元配列から多次元配列、構造体配列まで順番に確認していきましょう。
C言語の配列とは
配列の基本
配列とは、同じ型のデータを連続してまとめて扱うための仕組みです。
例えば10人分の点数や、1週間分の温度など、同じ種類の値を複数扱いたいときに使います。
単なる「変数の集まり」ではなく、メモリ上に連続した領域として確保され、添字(インデックス)で個々の要素にアクセスします。
典型的なイメージは次のようになります。
- 型が
intで要素数が5の配列aを用意すると、メモリ上にはa[0]からa[4]まで5つの整数が並びます。 - 添字は0から始まることに注意してください。
配列と変数の違い
配列と通常の変数の違いを整理しておきます。
代表的な点を挙げると、次のようになります。
- 通常の変数は1つの値を入れる箱です。
- 配列は同じ型の値を複数まとめて管理する箱で、要素ごとにインデックスでアクセスします。
- 配列名そのものは先頭要素へのポインタのように振る舞う場面がありますが、ここではまず「まとめて確保された要素群の名前」と捉えておくと理解しやすいです。
次のコードで違いを見てみましょう。
#include <stdio.h>
int main(void) {
int x; // 普通のint変数(1つだけ値を持つ)
int a[3]; // int型を3個まとめた配列
x = 10; // 変数xに代入
a[0] = 1; // 配列aの0番目の要素に代入
a[1] = 2; // 配列aの1番目の要素に代入
a[2] = 3; // 配列aの2番目の要素に代入
printf("x = %d\n", x);
printf("a[0] = %d\n", a[0]);
printf("a[1] = %d\n", a[1]);
printf("a[2] = %d\n", a[2]);
return 0;
}
x = 10
a[0] = 1
a[1] = 2
a[2] = 3
このように、配列は「添字付きの変数の集まり」として扱えます。
配列の宣言の書き方
配列の宣言の基本形は次のとおりです。
型名 配列名[要素数];
例えば、整数型intの要素を10個持つ配列を宣言するには、次のように書きます。
#include <stdio.h>
int main(void) {
int scores[10]; // int型の要素を10個持つ配列scoresを宣言
// ここではまだ値は入っていません(未初期化)
// 要素数はscores[0] ~ scores[9]の合計10個です
return 0;
}
ここで重要なのは添字が0から始まり、最大は要素数-1になるという点です。
上の例ではscores[0]からscores[9]までが有効な要素となり、それ以外の添字(例えばscores[10])にアクセスすると不正なアクセス(バッファオーバーフロー)になります。
配列の基本的な初期化方法
ここからは、配列の「初期化」について具体的に見ていきます。
初期化とは、宣言と同時に最初の値を与えることです。
代入と混同しがちですが、両者は厳密には異なります。
宣言と同時に初期化する書き方
最も基本的な初期化は、宣言と同時に{ }(波かっこ)で値を並べる方法です。
#include <stdio.h>
int main(void) {
// int型配列を宣言と同時に初期化
int a[5] = { 10, 20, 30, 40, 50 };
printf("a[0] = %d\n", a[0]);
printf("a[4] = %d\n", a[4]);
return 0;
}
a[0] = 10
a[4] = 50
この書き方では、並べた順番どおりに先頭から要素に代入されます。
要素数と値の個数をぴったり合わせるのが基本形です。
要素数を指定して初期化する
配列の要素数を明示して、それと同じ個数の値を並べるパターンです。
先ほどの例と同じですが、改めてポイントを整理します。
#include <stdio.h>
int main(void) {
// 要素数5を指定し、5個の値を並べて初期化
int nums[5] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 5; i++) {
printf("nums[%d] = %d\n", i, nums[i]);
}
return 0;
}
nums[0] = 1
nums[1] = 2
nums[2] = 3
nums[3] = 4
nums[4] = 5
この書き方の利点は、配列の大きさがコードから一目で分かることです。
後から配列を拡張したいときなどに、読みやすさの点で有利になります。
要素数を省略して初期化する
初期値から自動的に要素数を決めてもらうこともできます。
値の個数がそのまま要素数になります。
#include <stdio.h>
int main(void) {
// 要素数を省略し、初期値の数から自動的に要素数が決まる
int nums[] = { 10, 20, 30 };
// numsはint型要素3個の配列として扱われます
for (int i = 0; i < 3; i++) {
printf("nums[%d] = %d\n", i, nums[i]);
}
return 0;
}
nums[0] = 10
nums[1] = 20
nums[2] = 30
この方法は「初期値リストが真の定義」になるため、値の個数を増減させたときに要素数を自動で合わせてくれます。
その反面、要素数がコードから一見して分かりにくくなるため、どちらを選ぶかは状況に応じて決めると良いです。
途中まで値を指定した初期化
要素数を指定しつつ、すべての要素に値を与えない形の初期化もできます。
この場合、指定されなかった要素は0で初期化されます(静的記憶域と自動記憶域の両方でこのルールが適用されます)。
#include <stdio.h>
int main(void) {
// 要素数5だが、最初の3要素だけ初期化
int data[5] = { 1, 2, 3 }; // 残りの2要素は0で初期化される
for (int i = 0; i < 5; i++) {
printf("data[%d] = %d\n", i, data[i]);
}
return 0;
}
data[0] = 1
data[1] = 2
data[2] = 3
data[3] = 0
data[4] = 0
「残りは全部0で良い」という場面では、このパターンが簡潔で読みやすくなります。
すべて0で初期化する方法
すべての要素を0で初期化したい場合、{0}と1つだけ書く書き方がよく使われます。
#include <stdio.h>
int main(void) {
// 全要素を0で初期化する典型的な書き方
int a[5] = { 0 }; // a[0]~a[4]のすべてが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}と書くだけで残りすべてが0になることは、C言語の配列初期化で非常によく使われるテクニックです。
for文で1つ1つ代入するよりも安全で読みやすいため、覚えておくと便利です。
配列の初期化パターン
ここからは、型ごと・用途ごとに代表的な初期化パターンをまとめて紹介します。
int配列の初期化パターンまとめ
まずは整数配列(int配列)で、これまでの内容を整理します。
代表的な書き方を一覧表にまとめると次のようになります。
| パターン | 宣言・初期化の例 | 説明 |
|---|---|---|
| 全要素を指定 | int a[3] = { 1, 2, 3 }; | 要素数と値の数が一致する基本形 |
| 要素数省略 | int a[] = { 1, 2, 3 }; | 初期値の個数から要素数が決まる |
| 途中まで指定 | int a[5] = { 1, 2 }; | 残りはすべて0で初期化される |
| 全部0 | int a[5] = { 0 }; | 全要素が0になる定番パターン |
これらをまとめて試すサンプルです。
#include <stdio.h>
int main(void) {
int a[3] = { 1, 2, 3 }; // 全要素指定
int b[] = { 4, 5, 6 }; // 要素数省略
int c[5] = { 7, 8 }; // 残りは0
int d[5] = { 0 }; // 全部0
printf("配列a: ");
for (int i = 0; i < 3; i++) {
printf("%d ", a[i]);
}
printf("\n");
printf("配列b: ");
for (int i = 0; i < 3; 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 < 5; i++) {
printf("%d ", d[i]);
}
printf("\n");
return 0;
}
配列a: 1 2 3
配列b: 4 5 6
配列c: 7 8 0 0 0
配列d: 0 0 0 0 0
このように0埋めの性質を理解しておくと、配列の初期化がかなり楽になります。
char配列と文字列リテラルによる初期化
C言語では文字と文字列の扱いが配列と強く関係しています。
特にchar型と文字列リテラルの関係は重要です。
文字列リテラル"ABC"は、実際には'A'、'B'、'C'、'\0'(終端文字)の4文字分のchar配列として扱われます。
文字列リテラルで初期化する
#include <stdio.h>
int main(void) {
// 文字列リテラルでchar配列を初期化
char str1[] = "ABC"; // 要素数は4('A','B','C','#include <stdio.h>
int main(void) {
// 文字列リテラルでchar配列を初期化
char str1[] = "ABC"; // 要素数は4('A','B','C','\0')
char str2[10] = "Hello"; // 残りの要素は0で初期化される
printf("str1 = %s\n", str1);
printf("str2 = %s\n", str2);
return 0;
}
')
char str2[10] = "Hello"; // 残りの要素は0で初期化される
printf("str1 = %s\n", str1);
printf("str2 = %s\n", str2);
return 0;
}
str1 = ABC
str2 = Hello
要素数を省略すると、終端文字'\0'も含めた長さで自動的に決まる点がポイントです。
文字を1つずつ並べて初期化する
同じ結果を、文字を1つずつ並べる方法でも書けます。
#include <stdio.h>
int main(void) {
// 1文字ずつ指定して文字列を表現する
char str[4] = { 'A', 'B', 'C', '#include <stdio.h>
int main(void) {
// 1文字ずつ指定して文字列を表現する
char str[4] = { 'A', 'B', 'C', '\0' };
printf("str = %s\n", str);
return 0;
}
' };
printf("str = %s\n", str);
return 0;
}
str = ABC
この方法では必ず終端の'\0'を自分で入れる必要があるので、初心者のうちは文字列リテラルを使った方が安全です。
文字列配列(char配列の配列)の初期化
「文字列の一覧」を持たせたいときは、char配列の配列、すなわちchar 配列名[行数][1つの文字列の長さ]のように宣言します。
#include <stdio.h>
int main(void) {
// 3つの文字列を持つ配列
// 各文字列は最大9文字(+終端文字)まで
char words[3][10] = {
"apple",
"banana",
"cherry"
};
for (int i = 0; i < 3; i++) {
printf("words[%d] = %s\n", i, words[i]);
}
return 0;
}
words[0] = apple
words[1] = banana
words[2] = cherry
ここでは2次元配列を使っていますが、後述する「多次元配列の初期化」と同じルールで扱われます。
各行が1つの文字列になっていると考えると理解しやすいです。
より柔軟にするためにchar *配列で文字列リテラルへのポインタの配列にする方法もありますが、まずは固定長のchar[行数][列数]で慣れると良いです。
多次元配列(2次元配列)の初期化
2次元配列は、「配列の配列」と考えると理解しやすいです。
たとえばint a[2][3]は、要素数3のint配列が2つ並んだ構造になります。
2次元配列の基本的な初期化
#include <stdio.h>
int main(void) {
// 2行3列の2次元配列を初期化
int matrix[2][3] = {
{1, 2, 3}, // 1行目
{4, 5, 6} // 2行目
};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
}
}
return 0;
}
matrix[0][0] = 1
matrix[0][1] = 2
matrix[0][2] = 3
matrix[1][0] = 4
matrix[1][1] = 5
matrix[1][2] = 6
波かっこを省略した初期化
波かっこを1段だけにして、すべての要素を1列に書くこともできます。
#include <stdio.h>
int main(void) {
int matrix[2][3] = {
1, 2, 3,
4, 5, 6
};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
1 2 3
4 5 6
この場合も、内部的には行ごとに並んでいると考えて問題ありません。
ただし、行ごとに{ }を付けた方が行列の構造が分かりやすく、可読性の面でおすすめです。
構造体配列の初期化
構造体の配列も、配列と構造体それぞれの初期化ルールを組み合わせれば簡単に書けます。
典型例として、学生の名前と点数を持つ構造体を配列で管理してみます。
#include <stdio.h>
// 学生を表す構造体
typedef struct {
char name[20];
int score;
} Student;
int main(void) {
// 構造体Studentの配列を初期化
Student students[] = {
{ "Alice", 80 },
{ "Bob", 90 },
{ "Carol", 75 }
};
int count = sizeof(students) / sizeof(students[0]);
for (int i = 0; i < count; i++) {
printf("name = %s, score = %d\n",
students[i].name, students[i].score);
}
return 0;
}
name = Alice, score = 80
name = Bob, score = 90
name = Carol, score = 75
構造体配列の初期化は、要素ごとに{ ... }で構造体のフィールドを並べていく形になります。
複数の関連するデータをまとめて扱えるため、実用的なCプログラムではとてもよく使われるパターンです。
for文を使わない配列の簡単初期化テクニック
ここからは少し応用的なテーマとして、for文を使わずに配列を初期化するテクニックを紹介します。
C言語の初期化子のルールをうまく使うと、意外と柔軟な書き方ができます。
波かっこだけで連続した値をまとめて初期化
すでに見てきたように、単純な配列であれば波かっこで値を並べるだけで、一気に初期化できます。
#include <stdio.h>
int main(void) {
// 1から10までの連番をまとめて初期化
int numbers[10] = {
1, 2, 3, 4, 5,
6, 7, 8, 9, 10
};
for (int i = 0; i < 10; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
1 2 3 4 5 6 7 8 9 10
もちろん、この方法は「値が決まっている」配列に向いているやり方です。
何らかの計算に基づいて連番を作りたい場合はfor文を使う必要がありますが、「定数としての表」を用意するときには初期化子がとても便利です。
指定初期化子(インデックス指定)による部分初期化
C99以降では、指定初期化子(designated initializer)という機能を使って、配列の特定のインデックスだけに値をセットする書き方ができます。
#include <stdio.h>
int main(void) {
// 特定の要素だけを指定して初期化する(C99以降)
int a[10] = {
[0] = 1, // a[0]だけ1
[5] = 100, // a[5]だけ100
[9] = 999 // a[9]だけ999
// 他の要素は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] = 999
この指定初期化子を使うと、まばらに値が入っている「スパースな配列」を宣言時に簡潔に表現できます。
すべての要素を順番に並べなくても良いため、配列が大きい場合にも便利です。
また、多次元配列でも同様の書き方ができます。
#include <stdio.h>
int main(void) {
// 3x3の2次元配列で対角成分だけ1にする例
int mat[3][3] = {
[0][0] = 1,
[1][1] = 1,
[2][2] = 1
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
return 0;
}
1 0 0
0 1 0
0 0 1
必要なところだけ指定して、他は自動的に0で埋めてもらえるというイメージを持つと理解しやすいです。
マクロを使った配列初期化パターン
同じような初期化リストを何度も書きたくない場合は、マクロを使ってパターン化する方法があります。
#include <stdio.h>
// 1行分の初期化パターンをマクロで定義
#define ROW_INIT 1, 2, 3, 4, 5
int main(void) {
// マクロを使って配列を初期化
int a[5] = { ROW_INIT };
// 2次元配列にも流用できる
int b[2][5] = {
{ ROW_INIT },
{ ROW_INIT }
};
printf("配列a: ");
for (int i = 0; i < 5; i++) {
printf("%d ", a[i]);
}
printf("\n");
printf("配列b:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", b[i][j]);
}
printf("\n");
}
return 0;
}
配列a: 1 2 3 4 5
配列b:
1 2 3 4 5
1 2 3 4 5
このようにマクロで初期化リストをひとまとまりにしておくと、同じパターンを複数の配列に使い回したいときに便利です。
ただし、マクロは単純な置き換えであり、デバッグ時に追いにくくなることもあるため、多用しすぎないように注意してください。
初期化と代入の違いに注意するポイント
ここまで初期化の話をしてきましたが、「初期化」と「代入」は違うという点を改めて確認しておきましょう。
特に配列の場合、この違いが重要です。
配列はまとめて代入できない
次のコードを見てください。
#include <stdio.h>
int main(void) {
int a[3] = { 1, 2, 3 }; // 初期化(OK)
// これはコンパイルエラーになります(配列に=で代入はできない)
// int b[3];
// b = a; // エラー
// 正しくは要素ごとに代入する必要があります
int b[3];
for (int i = 0; i < 3; i++) {
b[i] = a[i]; // 要素単位での代入はOK
}
for (int i = 0; i < 3; i++) {
printf("b[%d] = %d\n", i, b[i]);
}
return 0;
}
b[0] = 1
b[1] = 2
b[2] = 3
配列は宣言時にしか「まとめて初期化」できないというルールを覚えておくと、エラーの原因を理解しやすくなります。
配列同士をコピーしたい場合は、上のようにfor文で要素ごとに代入するか、memcpyなどの関数を使う必要があります。
スコープとライフタイムにも注意
また、初期化は変数が宣言されたタイミングで1度だけ行われるという点も重要です。
関数の中で宣言したローカル配列は、関数が呼ばれるたびに初期化されます。
#include <stdio.h>
void func(void) {
// 毎回同じ内容で初期化される
int a[3] = { 1, 2, 3 };
for (int i = 0; i < 3; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
int main(void) {
func();
func(); // 2回呼んでも、毎回同じ出力になる
return 0;
}
1 2 3
1 2 3
「一度だけ初期化して、後は値を保持したい」といった場合は、staticを付けた静的変数を使う必要があります。
このあたりは配列のライフタイムの話になるため、配列に慣れてきたら合わせて学ぶと理解が深まります。
まとめ
配列の初期化は、C言語の中でも頻出でありながら混乱しやすいテーマです。
本記事ではint配列、char配列と文字列、多次元配列、構造体配列まで、代表的な初期化パターンを一つずつ丁寧に整理しました。
特に{0}による全要素0初期化や指定初期化子は実務でも非常によく使われます。
まずは宣言時にだけ使える「初期化」と、後から行う「代入」の違いを意識しながら、実際に手を動かして配列の初期化を試してみてください。
