C言語の基本|配列の基本

同じ型の値を、番号つきの箱へすっきり収納。配列を使えば、たくさんのデータをまとめて処理できます。

C言語でプログラムを作っていると、同じ種類の値を複数保存したい場面がたくさん出てきます。

たとえば、次のようなデータです。

  • 5人分のテスト結果
  • 7日分の気温
  • 10個分の商品価格
  • 複数回測定したセンサー値

配列を使わずに管理する場合は、データの数だけ変数を用意しなければなりません。

int score1;
int score2;
int score3;
int score4;
int score5;

この方法でも値は保存できますが、変数が増えるほど管理が大変になります。同じ処理をすべての変数に行いたい場合も、一つずつ処理を書かなければなりません。

そこで役立つのが配列です。

int scores[5];

この1行で、int型の値を5個保存するための領域をまとめて用意できます。各要素には番号が付いているため、for文などの繰り返し処理で順番に扱うこともできます。

配列は、同じ型の値を番号つきの箱に並べて保存する仕組みです。ここでは、1次元配列の宣言、代入、参照、初期化を順番に確認していきます。

配列とは

配列は、同じ型のデータを複数まとめて管理する仕組みです。

たとえば、次のような配列を作れます。

配列の宣言保存できる内容
int scores[5];int型の値を5個
double prices[10];double型の値を10個
char letters[20];char型の値を20個

1つの配列に入る要素は、すべて同じ型です。int型の配列なら各要素はint型、double型の配列なら各要素はdouble型になります。

配列に保存されている一つひとつの値を要素と呼びます。そして、どの要素を使うのかを指定する番号を添字と呼びます。

配列の要素と添字

C言語の配列では、添字が0から始まります。

要素数が5個なら、使用できる添字は0から4です。

要素の位置添字要素の指定
1番目0data[0]
2番目1data[1]
3番目2data[2]
4番目3data[3]
5番目4data[4]

人が数える位置と、C言語で指定する添字には1つのずれがあります。

1番目の要素を使うときはdata[1]ではなく、data[0]と書きます。5個の要素を持つ配列の最後はdata[4]です。

図1:配列の次元とデータの並び方

この図から分かること

配列には、データの並べ方を表す次元があります。

種類データの並び方イメージ
1次元配列一方向に並ぶ値の一覧
2次元配列行と列に並ぶ表や座席表
3次元配列縦・横・奥行きに並ぶ箱を立体的に積んだ構造

1次元配列では、添字を1つ使って要素を指定します。

data[0]

2次元配列では行と列に対応する2つの添字を使いますが、まずは添字を1つだけ使う1次元配列の仕組みを身につけましょう。

1次元配列とは

1次元配列は、同じ型の要素を一列に並べた配列です。

次の宣言では、int型の値を5個保存できる1次元配列dataを用意しています。

int data[5];

この配列には、次の5つの要素があります。

data[0]
data[1]
data[2]
data[3]
data[4]

配列名はdataですが、個々の要素を使うときは、配列名の後ろに角括弧と添字を付けます。

配列を使う基本的な流れ

配列を使う流れは、普通の変数とよく似ています。

手順内容
宣言配列の型、名前、要素数を決める
代入添字を指定して各要素に値を入れる
参照添字を指定して各要素の値を使う
初期化宣言と同時に最初の値を入れる

普通の変数は1つの値を扱いますが、配列では添字を使って複数の要素から1つを選びます。

配列を宣言する

配列の基本的な宣言は、次の形です。

型名 配列名[要素数];

int型の値を5個保存するなら、次のように宣言します。

int data[5];

それぞれの部分には、次の意味があります。

記述意味
int各要素の型
data配列名
5配列が持つ要素数

ほかの型でも同じ形で宣言できます。

double prices[10];
char letters[20];

配列を宣言しただけでは、各要素に使用できる値が入っているとは限りません。初期化していない配列の要素を、値を代入する前に参照しないようにしましょう。

要素数と最後の添字を区別する

次の配列の要素数は5です。

int data[5];

しかし、最後の添字は4です。

項目
要素数5
最初の添字0
最後の添字4

要素数をnとすると、使用できる添字は0からn - 1までです。

要素数使用できる添字
10
20~1
50~4
100~9

要素数と最後の添字を同じ値だと考えないように注意しましょう。

配列の要素に値を代入する

配列を宣言した後は、添字を指定して各要素に値を代入できます。

int data[5];

data[0] = 10;
data[1] = 20;
data[2] = 30;
data[3] = 40;
data[4] = 50;

代入の左側にあるdata[0]やdata[1]は、配列の中にある個別の要素を表しています。

data[0] = 10;

これは、dataの1番目の要素に10を代入するという意味です。

data[1] = 20;

こちらは、dataの2番目の要素に20を代入します。

図2:要素数5の配列と添字の対応

この図から分かること

要素数が5個の配列では、添字は0、1、2、3、4となります。

配列の要素は、次のように対応します。

  • data[0]は1番目
  • data[1]は2番目
  • data[2]は3番目
  • data[3]は4番目
  • data[4]は5番目

data[5]は6番目を指定することになるため、要素数5の配列では使用できません。

配列の要素を参照する

配列に保存した値を計算や表示に使うことを、要素の参照といいます。

printf("%d\n", data[0]);
printf("%d\n", data[1]);

data[0]には10が入っているため、最初のprintfは10を表示します。data[1]には20が入っているため、次のprintfは20を表示します。

配列の要素は、普通の変数と同じように計算にも使えます。

int total;

total = data[0] + data[1];

この場合は10 + 20が計算され、totalに30が代入されます。

配列名だけでは、どの要素を使いたいのかを表せません。個々の値を代入したり参照したりするときは、配列名と添字を組み合わせます。

配列を宣言と同時に初期化する

配列は、宣言と同時に複数の値を入れられます。

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

波括弧の中に並べた値が、添字の小さい順に各要素へ入ります。

要素初期値
data[0]10
data[1]20
data[2]30
data[3]40
data[4]50

初期値をすべて書く場合は、要素数を省略できます。

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

初期値が5個あるため、コンパイラが要素数を5と判断します。

初期化の書き方を整理する

配列の初期化には、次のような書き方があります。

記述意味
int a[3] = {1, 2, 3};要素数3で、すべての初期値を指定する
int a[] = {1, 2, 3};初期値の個数から要素数を決める
int a[5] = {1, 2};最初の2要素に値を入れ、残りを0にする
int a[5] = {0};すべての要素を0にする

次の初期化では、指定されていない要素が0になります。

int a[5] = {1, 2};

各要素の値は次のとおりです。

要素
a[0]1
a[1]2
a[2]0
a[3]0
a[4]0

配列をすべて0で初期化したい場合は、次の書き方が便利です。

int a[5] = {0};

初期化と代入の違い

初期化と代入は、値を入れるタイミングが異なります。

操作タイミング
初期化配列を宣言するときint a[] = {1, 2, 3};
代入配列を宣言した後a[0] = 1;

初期化では、複数の初期値を波括弧の中にまとめて書けます。

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

宣言後に値を変更するときは、それぞれの要素を指定して代入します。

a[0] = 10;
a[1] = 20;
a[2] = 30;

宣言後に、配列全体へ波括弧を使ってまとめて代入することはできません。宣言後は個々の要素に代入します。

配列の基本操作をプログラムで確認する

ここでは、4日分の発送件数を配列に保存します。

1つ目の配列では、宣言した後に各要素へ値を代入します。2つ目の配列では、宣言と同時に初期化します。

発送件数の配列を宣言・代入・初期化するプログラム

ファイル名:10_1_1.c

#include <stdio.h>

int main(void)
{
    /* 4日分の発送件数を保存する配列を宣言する */
    int shipments[4];

    /* 初期値から要素数が決まる配列を作る */
    int shipments2[] = {8, 12, 7, 15};

    /* shipmentsの各要素に値を代入する */
    shipments[0] = 8;
    shipments[1] = 12;
    shipments[2] = 7;
    shipments[3] = 15;

    /* shipmentsの要素を1つずつ参照して表示する */
    printf("%d\n", shipments[0]);
    printf("%d\n", shipments[1]);
    printf("%d\n", shipments[2]);
    printf("%d\n", shipments[3]);

    /* 初期化した配列の全要素を表示する */
    printf("%d %d %d %d\n",
           shipments2[0], shipments2[1],
           shipments2[2], shipments2[3]);

    return 0;
}
実行結果の例
8
12
7
15
8 12 7 15

配列の宣言を確認する

最初に、要素数4の配列shipmentsを宣言しています。

int shipments[4];

この配列で使用できる要素は次の4つです。

shipments[0]
shipments[1]
shipments[2]
shipments[3]

宣言した時点では、まだ各要素に使用する値を設定していません。この後の代入によって、すべての要素へ値を入れています。

配列の要素への代入を確認する

次の部分では、shipmentsの各要素に発送件数を代入しています。

shipments[0] = 8;
shipments[1] = 12;
shipments[2] = 7;
shipments[3] = 15;

それぞれの対応は次のとおりです。

日数要素発送件数
1日目shipments[0]8
2日目shipments[1]12
3日目shipments[2]7
4日目shipments[3]15

1日目がshipments[1]ではなく、shipments[0]である点を確認しておきましょう。

配列の初期化を確認する

次の配列は、宣言と同時に値を入れています。

int shipments2[] = {8, 12, 7, 15};

角括弧の中に要素数を書いていませんが、初期値が4個あるため、要素数は自動的に4になります。

これは次の書き方と同じ要素数です。

int shipments2[4] = {8, 12, 7, 15};

どちらの書き方でも、使用できる添字は0から3です。

配列の要素の参照を確認する

次のprintfでは、shipmentsの要素を1つずつ参照しています。

printf("%d\n", shipments[0]);

shipments[0]には8が保存されているため、8が表示されます。

shipments2では、1つのprintfに4つの要素を指定しています。

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

このように、同じ配列の要素でも、それぞれを別の値としてprintfへ渡せます。

添字の範囲外アクセスに注意する

配列を使うときに特に注意したいのが、添字の範囲外アクセスです。

次の配列を考えてみましょう。

int data[5];

使用できる要素はdata[0]からdata[4]です。

次の指定は範囲外です。

data[5] = 100;
data[-1] = 200;

C言語では、配列の範囲外を使っても、必ず分かりやすいエラーメッセージが出るとは限りません。別のデータを壊したり、予想外の結果になったりする可能性があります。

配列正しい添字範囲外の例
int a[3];0~2-1、3
int b[5];0~4-1、5
int c[10];0~9-1、10

要素数がnなら、添字は0以上n未満にします。

0 <= 添字 && 添字 < 要素数

配列とfor文を組み合わせる

配列は、for文と組み合わせることで便利さを発揮します。

shipments2の4要素を順番に表示するなら、次のように書けます。

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

iは0、1、2、3と変化します。そのため、次の要素が順番に参照されます。

shipments2[0]
shipments2[1]
shipments2[2]
shipments2[3]

継続条件をi <= 4ではなく、i < 4と書いている点が重要です。

i <= 4ではiが4のときもループ本体が実行され、shipments2[4]を参照してしまいます。要素数4の配列に添字4は存在しません。

図3:for文で配列を先頭から順番に処理する

この図から分かること

for文のカウンタ変数iを添字として使うと、配列の全要素を先頭から順番に処理できます。

要素数が4なら、iは0から3まで変化させます。

iの値参照する要素
0shipments[0]
1shipments[1]
2shipments[2]
3shipments[3]

継続条件はi < 4です。iが4になると条件が偽になるため、範囲外のshipments[4]は参照されません。

配列を使わない場合と比較する

4日分の発送件数を個別の変数で管理すると、次のようになります。

int shipment1 = 8;
int shipment2 = 12;
int shipment3 = 7;
int shipment4 = 15;

配列なら、次のように1つのまとまりとして表現できます。

int shipments[] = {8, 12, 7, 15};

違いを整理すると、次のようになります。

観点個別の変数配列
データの管理別々の名前で管理する1つの配列名でまとめる
値の指定変数名を使う配列名と添字を使う
繰り返し処理まとめて処理しにくいfor文で順番に処理できる
データ数が増えた場合変数名を追加する要素数や初期値を変更する
向いている処理別々の意味を持つ値同じ型で同じ目的の値

配列は、単に変数名を短くする仕組みではありません。同じ役割を持つ複数の値を1つのまとまりとして扱い、添字によって順番に処理できる点が大きな特徴です。

普通の変数と配列を比べる

普通の変数は1つの値を保存します。配列は同じ型の値を複数保存します。

項目普通の変数配列
保存できる値1個複数個
宣言例int score;int scores[5];
値の指定方法scorescores[0]
主な用途単独の値を扱う同じ種類のデータをまとめる
ループとの相性個別の変数名では扱いにくい添字を変化させて処理できる

配列の各要素は、普通の変数と同じように値の代入や参照ができます。違うのは、どの要素を使うかを添字で指定する点です。

1次元配列の基本的な書き方

1次元配列でよく使う書き方を整理すると、次のようになります。

目的書き方
配列を宣言するint data[5];
要素に代入するdata[0] = 10;
要素を参照するprintf("%d\n", data[0]);
宣言時に初期化するint data[] = {10, 20, 30};
一部だけ初期化するint data[5] = {10, 20};
すべて0で初期化するint data[5] = {0};
for文で順番に参照するdata[i]

この中でも、最初は次の4つを確実に使えるようにしましょう。

  • 宣言
  • 代入
  • 参照
  • 初期化

配列でつまずきやすいポイント

配列を学び始めたときは、次のような間違いが起こりやすくなります。

つまずきやすい点正しい考え方
添字を1から始める最初の添字は0
要素数5なら添字5まで使えると思う使用できるのは0~4
配列名だけで個別の値を指定する配列名と添字を組み合わせる
宣言しただけで0が入ると思う初期化していなければ値は決まっていない
範囲外でも安全だと思う予想外の動作やバグの原因になる
for文の条件をi <= 要素数にするi < 要素数とする

添字を使うコードでは、要素数と添字の範囲をセットで確認する習慣を付けると安心です。

配列を読むときの確認ポイント

配列を含むプログラムを読むときは、次の順番で確認すると理解しやすくなります。

  • 配列の型は何か
  • 配列名は何か
  • 要素数はいくつか
  • 使用できる添字はどこからどこまでか
  • どの要素に値を代入しているか
  • どの要素を参照しているか
  • 宣言時に初期化されているか
  • for文の添字が範囲内に収まっているか

配列は、同じ型の値を番号つきの箱へ並べる仕組みです。要素数、添字、代入、参照の関係を一つずつ確認できれば、1次元配列のコードはぐっと読みやすくなります。