C言語のきほん|配列の宣言と初期化

配列は、最初の値の入れ方でぐっと分かりやすくなる。宣言と初期化を覚えると、C言語の配列操作がもっと自然になります

配列を使い始めると、まず大切になるのが「どうやって配列を用意するか」と「最初の値をどう入れるか」という2つの考え方です。
ただ宣言するだけでも配列は作れますが、その直後の配列要素には意味のある値が入っているとは限りません。
そのため、必要に応じて最初から値を入れておく 初期化 がとても重要になります。

特にC言語では、宣言しただけのローカル配列は不定値を持つことがあります。
この状態のまま使ってしまうと、思いがけない結果になったり、バグの原因になったりします。
そこで、配列を作ると同時に必要な値を順番に入れておく書き方を身につけておくと、プログラムがずっと安全で分かりやすくなります。

配列の初期化にはいくつかの書き方があります。
要素数と同じ数だけ値を並べる基本形はもちろん、要素数を省略する書き方、足りない分を自動で 0 にする書き方、さらに特定の位置だけを初期化する少し発展的な書き方もあります。

ここでは、配列の宣言と初期化を中心に、1次元配列の最初の扱い方をていねいに整理していきます。
基本の書き方から、初期化子の意味、要素数との関係、便利な省略記法、0 初期化、要素指示子まで、順番に見ていきましょう。

配列の宣言と初期化は何が違うのか

まず最初に、宣言初期化 の違いをはっきりさせておくと理解しやすくなります。

項目意味
宣言配列を使えるように用意すること
初期化配列を作ると同時に最初の値を入れること

たとえば、次のコードは宣言です。

int data[5];

一方、次のコードは初期化です。

int data[5] = {10, 20, 30, 40, 50};

どちらも配列を作っていますが、後者は 配列を作ると同時に値も入れている ところが大きな違いです。

宣言しただけの配列は不定値になることがある

C言語では、関数の中で宣言した配列は、何も初期化しなければ中身が不定値になることがあります。
不定値とは、どんな値が入っているか決まっていない状態のことです。

たとえば次のような配列を考えてみます。

int scores[5];

この時点では、scores[0] から scores[4] までに意味のある値が入っているとは限りません。
そのため、そのまま表示したり計算に使ったりするのは危険です。

イメージとしては、次のような状態です。

配列 scores
[0] 不定値
[1] 不定値
[2] 不定値
[3] 不定値
[4] 不定値

だからこそ、必要に応じて初期化を行うことが大切になります。

1次元配列の初期化の基本文法

1次元配列を初期化するときは、次の形で書きます。

型名 配列名[要素数] = {値1, 値2, 値3, ...};

この文法を表で整理すると、次のようになります。

部分内容
型名配列の要素の型
配列名配列につける名前
要素数配列が持つ要素の数
=初期値を与えるための記号
{ } の中の値初期化子

ここで、{ } の中に書く値を 初期化子 と呼びます。
初期化子は、先頭から順番に配列要素へ入っていきます。

初期化子は先頭から順番に入る

たとえば次のように書いたとします。

int sales[5] = {120, 150, 90, 200, 170};

この場合、配列の中身は次のようになります。

添字
0120
1150
290
3200
4170

つまり、初期化子は書いた順に、先頭の要素から並んで入っていきます。

こんなイメージです。

要素数と初期化子の数が等しい場合

もっとも基本的な初期化の形が、要素数と初期化子の数がぴったり一致する場合です。

記述例

int visitors[5] = {12, 15, 9, 18, 20};

この場合は、5個の要素を持つ配列 visitors に、5個の初期化子が順番に入ります。

状態

添字
012
115
29
318
420

この形は一番分かりやすく、学習の最初にもおすすめです。

初期化子があるときは要素数を省略できる

配列の初期化では、初期化子が書かれているときに限って、要素数を省略できます。

記述例

int visitors[] = {12, 15, 9, 18, 20};

この場合、コンパイラが初期化子の個数を数えて、自動的に要素数を 5 と判断してくれます。

つまり、次の2つは同じ意味になります。

int visitors[5] = {12, 15, 9, 18, 20};
int visitors[]  = {12, 15, 9, 18, 20};

この書き方の便利なところは、要素数を手で数えなくてもよいことです。
特に初期値がたくさんあるときは、要素数の書き間違いを防ぎやすくなります。

要素数を省略できるのは初期化子があるときだけ

ここはとても大切です。
要素数の省略は、初期化子が書かれているときだけ可能です。

たとえば、これは正しいです。

int numbers[] = {1, 2, 3};

でも、これは正しくありません。

int numbers[];

初期化子がないと、要素数が何個なのか分からないからです。

つまり、要素数を省略できるのは、初期化子から個数を判断できる場合だけだと覚えておくと安心です。

要素数より初期化子が少ない場合

配列の初期化では、初期化子の数が要素数より少なくても構いません。
このとき、足りない要素は自動的に 0 で初期化されます。

記述例

int visitors[5] = {12, 15, 9};

この場合、配列の中身は次のようになります。

添字
012
115
29
30
40

つまり、書かれていない後ろの要素には自動で 0 が入ります。

図にすると、こんなイメージです。

これはC言語の配列初期化でとても便利な性質です。

配列全体を 0 で初期化する書き方

この性質を利用すると、配列全体を簡単に 0 で初期化できます。

記述例

int visitors[5] = {0};

この場合、最初の要素に 0 が入り、残りの要素も足りない分として自動的に 0 になります。
結果として、すべての要素が 0 になります。

添字
00
10
20
30
40

この書き方はとてもよく使われます。
配列の全要素をまとめて 0 にしたいときに便利です。

初期化子の数は要素数を超えてはいけない

初期化子の数は、要素数以下でなければなりません。
要素数より多い初期化子を書くことはできません。

たとえば次のような書き方は問題になります。

int values[3] = {10, 20, 30, 40};

この配列は要素数が 3 なのに、初期化子が 4 個あります。
このような書き方は不正です。

そのため、初期化では次の関係を守る必要があります。

初期化子の数 ≦ 要素数

要素指示子を使った初期化

C99以降では、要素指示子 を使って特定の位置から初期化することもできます。
これは少し発展的ですが、とても便利な書き方です。

記述例

int scores[5] = {[2] = 78, 69};

この書き方では、まず scores[2] に 78 が入ります。
そして、その次の要素である scores[3] に 69 が入ります。
それ以外の要素は 0 で初期化されます。

状態

添字
00
10
278
369
40

このように、要素指示子を使うと、特定の位置に値を置きたいときに便利です。

要素指示子の動きをもう少し丁寧に見る

次のコードを順番に考えてみましょう。

int scores[5] = {[2] = 78, 69};

このときの流れはこうなります。

  1. 配列 scores の要素数は 5
  2. [2] = 78 によって scores[2] に 78 を入れる
  3. その次の位置である scores[3] に 69 を入れる
  4. 指定されていない要素は 0 になる

このため、結果は次のようになります。

配列 scores
[0]  0
[1]  0
[2] 78
[3] 69
[4]  0

この書き方は、途中の要素だけ値を入れたいときに便利です。

サンプルプログラムで見てみよう

5日分の来店人数を扱う例です。

サンプルプログラム

ファイル名:10_3_1.c

#include <stdio.h>

int main(void)
{
    // 要素数と同じ数の初期化子で配列を初期化する
    int visitors1[5] = {12, 15, 9, 18, 20};

    // 初期化子があるので要素数を省略して配列を初期化する
    int visitors2[] = {8, 11, 14, 10, 13};

    // 初期化子が少ないので、残りの要素は0で初期化される
    int visitors3[5] = {5, 7, 9};

    // 配列全体を0で初期化する
    int visitors4[5] = {0};

    // 要素指示子を使って特定の位置から初期化する
    int visitors5[5] = {[2] = 21, 24};

    printf("visitors1: %d %d %d %d %d\n",
           visitors1[0], visitors1[1], visitors1[2], visitors1[3], visitors1[4]);

    printf("visitors2: %d %d %d %d %d\n",
           visitors2[0], visitors2[1], visitors2[2], visitors2[3], visitors2[4]);

    printf("visitors3: %d %d %d %d %d\n",
           visitors3[0], visitors3[1], visitors3[2], visitors3[3], visitors3[4]);

    printf("visitors4: %d %d %d %d %d\n",
           visitors4[0], visitors4[1], visitors4[2], visitors4[3], visitors4[4]);

    printf("visitors5: %d %d %d %d %d\n",
           visitors5[0], visitors5[1], visitors5[2], visitors5[3], visitors5[4]);

    return 0;
}

実行結果の例

visitors1: 12 15 9 18 20
visitors2: 8 11 14 10 13
visitors3: 5 7 9 0 0
visitors4: 0 0 0 0 0
visitors5: 0 0 21 24 0

このサンプルで確認したいこと

このプログラムには、配列初期化の代表的な書き方がまとめて入っています。
1つずつ意味を確認していきましょう。

visitors1 の初期化

int visitors1[5] = {12, 15, 9, 18, 20};

要素数 5 に対して初期化子も 5 個です。
もっとも基本的な形で、書いた順にすべての要素へ値が入ります。

visitors2 の初期化

int visitors2[] = {8, 11, 14, 10, 13};

初期化子があるので要素数を省略しています。
コンパイラが自動的に 5 個の要素を持つ配列だと判断します。

visitors3 の初期化

int visitors3[5] = {5, 7, 9};

初期化子は 3 個だけですが、残り 2 個は自動で 0 になります。
そのため、5, 7, 9, 0, 0 という並びになります。

visitors4 の初期化

int visitors4[5] = {0};

これは全要素を 0 にしたいときの定番です。
最初の 0 のあと、残りも自動的に 0 で埋まります。

visitors5 の初期化

int visitors5[5] = {[2] = 21, 24};

これは要素指示子を使った初期化です。
visitors5[2] に 21、その次の visitors5[3] に 24 が入り、それ以外は 0 になります。

宣言だけの場合と初期化ありの場合を比べよう

この違いを意識すると、初期化の大切さがよく見えてきます。

宣言だけ

int data[5];

この場合、各要素は不定値の可能性があります。

初期化あり

int data[5] = {0};

この場合、すべての要素が 0 になります。

つまり、あとで使うことが分かっている配列は、できるだけ意図のある初期値を入れておくと安全で分かりやすい ということです。

配列の初期化を表で整理しよう

書き方意味
int a[5] = {1, 2, 3, 4, 5};すべての要素を順番に初期化
int a[] = {1, 2, 3, 4, 5};初期化子の数から要素数を自動決定
int a[5] = {1, 2, 3};残りは 0 になる
int a[5] = {0};全要素を 0 にする
int a[5] = {[2] = 7, 8};2番目の添字から特定位置を初期化

この表を手元に置いておくと、書き方の違いを整理しやすいです。

配列の初期化で意識したいこと

配列の初期化では、次の3点を意識すると理解しやすくなります。

視点内容
何個の要素を持つか要素数を見る
どこに何が入るか初期化子の並びを見る
足りない分はどうなるか自動で 0 になるかを見る

とくに、足りない分は 0 になる という性質は、実際のプログラムでもよく使います。

よくある注意点

配列の宣言と初期化では、次のような点に注意が必要です。

注意点内容
宣言だけでは不定値のことがある使う前に値を入れる
初期化子が多すぎるのはだめ要素数を超えないようにする
要素数の省略は初期化子があるときだけ宣言だけでは省略できない
添字は 0 から始まる要素数 5 なら最後は 4
要素指示子は C99以降古い規格では使えない場合がある

配列の宣言と初期化は今後の土台になる

配列の宣言と初期化は、配列学習の入口ですが、ここで理解したことはこの先ずっと使います。
for文で順番に処理するときも、2次元配列を学ぶときも、文字列を扱うときも、まずは「どんな配列をどう用意したか」が出発点になります。

最初は書き方がいくつかあって少しややこしく感じるかもしれません。
でも、考え方はとても素直です。

  • まず配列を用意する
  • 必要なら最初から値を入れる
  • 値は先頭から順番に入る
  • 足りない分は 0 になることがある

この流れがつかめると、配列はぐっと身近になります。
宣言と初期化をしっかり理解しておくと、その後の配列操作も自然に読めるようになります。