Lesson 4
配列
Lesson 4
Chapter 1
配列
C言語で大量のデータや、多数の値を管理するときには、配列が役立ちます。配列には同じ型の値を並べて格納することが可能で、指定した位置の値を読み書きできます。本Lessonでは、配列の使い方や仕組みを学習します。配列の宣言、値の読み書き、初期化、コピーの方法を学びましょう。また、Lesson3では、繰り返し文を学びましたが、これを活用して配列を操作する方法も解説します。さらに、多次元配列といった配列のバリエーションも紹介します。
配列とは
配列(はいれつ)は、同じ型の値を多数並べて管理できる機能です。配列は要素(ようそ)が集まってできています。要素は1つでも複数でもよいのですが、実用上は複数の要素からなる配列を使うことが多いでしょう。配列では、配列中の指定した要素に値を書き込んだり、指定した要素から値を読み出したりできます。配列と繰り返し文を組み合わせると、簡潔なプログラムで多数の値を処理することが可能です。まずは配列について学ぶ前に、通常の変数で多数の値を扱おうとすると何が困るのか、考えてみます。6つの変数を宣言してキーボードから入力した6つの整数を格納した後、全ての変数の値を表示するプログラムを書いてみましょう。
array1.c
#include <stdio.h>
int main(void) {
int a,b,c,d,e;
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
scanf("%d", &d);
scanf("%d", &e);
printf("%d ", a);
printf("%d ", b);
printf("%d ", c);
printf("%d ", d);
printf("%d ", e);
puts("");
return 0;
}
実行結果
array1.exe
560
336
784
31
294
560 336 784 31 294
上記のプログラムでは、入力用のscanf関数と、出力用のprintf関数を5ずつ回呼び出しています。同じような処理を何度も書かなければいけないので、プログラムが長くなってしまいます。さらに値の個数が増えた場合、整数の数だけ大変になります。もし1000個、10000個といった値を扱う場合、この方法ではプログラムが長くなりぎるでしょう。後で修正したり、間違いがないか確認したりするのが負担になってしまいます。そこで役立つのが配列です。
変数と同様に、配列は使う前に宣言しておく必要があります。配列の宣言は次のように書きます。変数の宣言に似ていますが、[](角括弧)で囲んで、「要素数」(要素の個数)を指定する点が異なります。配列名は識別子の一種で、変数と同様に名前をつけます。
配列の宣言
型 配列名[要素数];
複数の配列を同時に宣言することもできます。
配列の宣言(複数)
型 配列名A[要素数A], 配列名B[要素数B], …;
配列の要素数は整数で指定します。具体的には以下のように、いくつかの指定方法があります。
整数
10や20といった整数をそのまましてする方法です。
列挙体の定数
列挙体(Lesson6)を使って、例えばA=5のように定数を宣言しておき、このAを要素数にする方法です。
演算子を使った式
上記の整数や列挙体の定数に、演算子(代入、インクリメント、デクリメント、カンマを除く)を組み合わせた式を、要素数にする方法です。式の値は整数になる必要があり、かつコンパイル時に値が決まる必要があります。このような式のことを「整数定数式」と呼びます。
インデックス(添字)
配列は、特定の型の値を格納できる要素が並んだ構造をしています。宣言時に決めた配列の要素数は、後から増減することはできません。以下は5つの要素を持つint型の配列です。変数名はstockになります。要素数は「インデックス」または「添字」(そえじ)と呼ばれる機能で指定します。インデックスは[0]から始まり[1]、[2]、[3]… のように1ずつ増加する整数です。
インデックスの範囲は「0」から「要素数 - 1」までです。配列の範囲外を指すインデックスを使って、要素を読み書きしてはいけません。読み書きしてしまうと、メモリアクセス違反が起きてプログラムが終了したり、関係のない変数や配列の値を書き換えてしまう恐れがあります。インデックスが配列の範囲内を指しているかどうかは、コンパイラがチェックできない場合が多く、プログラマが注意しなければなりません。では、5つの要素を持つ配列を宣言して、キーボードから入力した5個の整数を各要素に格納した後に、全要素の値を出力するプログラムを書いてみましょう。
array2.c
#include <stdio.h>
int main(void) {
int stock[5];
scanf("%d", &stock[0]);
scanf("%d", &stock[1]);
scanf("%d", &stock[2]);
scanf("%d", &stock[3]);
scanf("%d", &stock[4]);
printf("%d ", stock[0]);
printf("%d ", stock[1]);
printf("%d ", stock[2]);
printf("%d ", stock[3]);
printf("%d ", stock[4]);
puts("");
return 0;
}
実行結果
array2.exe
560
336
794
31
294
560 336 794 31 294
変数を配列stockにまとめることで「array1.c」では、5つの変数を宣言したところを1つにすることができました。scanf関数とprintf関数が5回呼ばれていますが、繰り返し処理を組み合わせることでコードを短くまとめることができます。
for文による配列の操作
配列は繰り返しとの相性が良い機能です。配列中のどの要素にアクセスしたいのかは、インデックスを使って指定しますが、インデックスに式を書くことができます。ただし、この式の値は整数である必要があります。ここにループ処理を含む式を書けば、繰り返しを使って多くの要素を順に処理することが可能です。繰り返し文(for文)を使って、前回のプログラム「array2.c」を簡潔にします。5回呼び出しているscanf関数とprintf関数を、for文による繰り返しに書き換えて、プログラムを短くしてみましょう。
array3.c
#include <stdio.h>
int main(void) {
int stock[5];
for(int i = 0;i < 5; i++) scanf("%d", &stock[i]);
for(int i = 0;i < 5; i++) printf("%d ", stock[i]);
puts("");
return 0;
}
実行結果
array3.exe
560
336
794
31
294
560 336 794 31 294
配列の初期化
配列の初期化には、いくつかの方法がありますが、最も基本的なのは次の記法です。{}(波括弧)の中に、配列に格納する複数の値を、,(カンマ)で区切って書きます。配列の要素数は省略して、[](角括弧)だけを書いていることに注目してください。配列の要素数は、格納する値の個数に合わせて自動的に決まります。
配列の初期化
型 配列名[] = {値1, 値2, 値3, …};
上記の文法を用いることにより、配列を定義し、各配列要素へ在庫(stock)の値を格納する操作を1行で記述することができます。実際にint型の配列stockの初期化を行い、格納した各要素を表示してみましょう。
array4.c
#include <stdio.h>
int main(void) {
int stock[] = {560, 336, 794, 31, 294};
for(int i = 0;i < 5; i++) printf("%d ", stock[i]);
puts("");
return 0;
}
実行結果
array4.exe
560 336 794 31 294
配列の要素数と、初期化で格納する値の個数が異なる場合は、次のように要素数を指定します。値は先頭の要素から順に格納され、値が指定されなかった要素の値は「0」になります。
配列の初期化
型 配列名[要素数] = {値1, 値2, 値3, …};
要素数が5個でint型の配列stockを560、336、794で初期化した後に、各要素の値を出力するプログラムを書いてみましょう。
array5.c
#include <stdio.h>
int main(void) {
int stock[5] = {560, 336, 794};
for(int i = 0;i < 5; i++) printf("%d ", stock[i]);
puts("");
return 0;
}
実行結果
array5.exe
560 336 794 0 0
配列の要素数
配列は、メモリ上に要素が隙間なく並んだ構造をしています。例えば1個の要素が4バイト(int型)で、要素数が5個ならば、配列全体は20バイトです。
配列全体や要素のバイト数は、sizeof演算子で調べられます。次のように、sizeof演算子に式を指定すると、その式の型のバイト数を返します。
式の型のバイト数
sizeof 式
配列全体や要素のバイト数を調べるには、次のように書きます。要素に関しては、どの添字を指定しても同じバイト数になります。
配列全体のバイト数
sizeof 配列名
要素のバイト数
sizeof 配列名[インデックス]
配列全体や要素のバイト数を調べてみましょう。要素数が5個でint型の配列stockを宣言し、配列全体のバイト数、要素のバイト数、要素数を表示するプログラムを書いてください。要素数については、配列全体のバイト数を、要素のバイト数で割って求めてください。printf関数の変換指定子には「%lu」を使います。
array6.c
#include <stdio.h>
int main(void) {
int stock[5];
puts("配列全体のバイト数を表示");
printf("%lu\n", sizeof stock);
puts("要素のバイト数を表示");
printf("%lu\n", sizeof stock[0]);
puts("要素数を表示");
printf("%lu\n", sizeof stock / sizeof stock[0]);
return 0;
}
実行結果
array6.exe
配列全体のバイト数を表示
20
要素のバイト数を表示
4
要素数を表示
5
配列のコピー
通常の変数の値は代入でコピーできますが、配列同士は代入できません。配列の要素ごとにコピーする方法と、メモリごとコピーする方法を紹介します。
要素ごとにコピーする
配列の要素を、別の配列の要素に代入してコピーすることが可能です。繰り返しを使って配列の全要素を順にコピーすれば、配列を全てコピーできます。int型の配列stock1を560、336、794、31、294で初期化した後に、要素数が5個でint型の配列stock2にコピーするプログラムを書いて配列のコピーをしてみましょう。結果を確認するために、stock1とstock2を出力します。
array7.c
#include <stdio.h>
int main(void) {
int stock1[] = {560, 336, 794, 31, 294};
for(int i = 0;i < 5; i++) printf("%d ", stock1[i]);
puts("");
int stock2[5];
for(int i = 0;i < 5; i++) stock2[i] = stock1[i];
for(int i = 0;i < 5; i++) printf("%d ", stock2[i]);
puts("");
return 0;
}
実行結果
array7.exe
560 336 794 31 294
560 336 794 31 294
メモリごとコピーする
標準ライブラリの「memcpy」(メムコピー)は、指定したメモリの内容をコピーする関数です。memcpy関数を使うには、string.hのインクルードが必要です。memcpy関数は次のように使います。
memcpy関数
memcpy(コピー先のアドレス, コピー元のアドレス, バイト数);
memcpy関数を使って、配列のメモリを丸ごとコピーすれば、ある配列を別の配列にコピーできます。上記のアドレスには配列の先頭アドレスを、バイト数には配列のバイト数を指定します。memcpy関数の構文は、アドレスには「配列名」を指定して、バイト数は「sizeof 配列名」で取得できます。プログラム「array6.c」を修正して、memcpy関数で配列をコピーしてみましょう。配列を要素ごとにコピーする処理を、memcpy関数の呼び出しに書き換えてください。
array8.c
#include <stdio.h>
#include <string.h>
int main(void) {
int stock1[] = {560, 336, 794, 31, 294};
for(int i = 0;i < 5; i++) printf("%d ", stock1[i]);
puts("");
int stock2[5];
memcpy(stock2, stock1, sizeof stock2);
for(int i = 0;i < 5; i++) printf("%d ", stock2[i]);
puts("");
return 0;
}
実行結果
array8.exe
560 336 794 31 294 ←コピー元 stock1
560 336 794 31 294 ←コピー先 stock2
配列をコピーする2種類の方法を学習しましたが、どちらの方法を使っても構いません。処理速度を考慮する場合は、両方の実行時間を計測して比較してみます。ケースによりますが、memcpy関数は高速なコピーができるように調整されている可能性があります。一方で、要素ごとにコピーする方法は応用性に優れており、例えば、要素を逆の順番でコピーしたいなどのケースに対応できます。ここまでに登場した配列は、要素が一直線上に並んでいるイメージでした。これらは「1次元配列」と呼ばれます。次のChapterでは2次元以上の配列「多次元配列」を学習します。

Lesson 4
Chapter 2
多次元配列
多次元配列とは
多次元配列は次のように宣言します。以下は2次元配列の場合ですが、[](角括弧)による要素数の指定を増やせば、配列の次元を増やせます。
多次元配列の宣言
型 配列名[要素数A][要素数B];
上記の図は要素数aが配列の行を、要素数bが配列の列を表しています。図の多次元配列を宣言するときは、3行、3列からなる多次元配列なので、以下のように[](角括弧)でインデックスを記述します。
多次元配列の宣言(各要素数が3の場合)
int hairetu[3][3];
多次元配列を初期化するには、{}(波括弧)をネストして書く必要があります。内側の{}が、上記の図を配列の各行に対応します。
多次元配列の要素数
多次元配列の要素のバイト数を調べてみましょう。要素数が3行、5列でint型の配列stockを宣言し、行の要素数、列の要素数、配列全体の要素数を表示するプログラムを書いてください。printf関数の変換指定には「%1u」を使います。
array9.c
#include <stdio.h>
int main(void) {
int stock[3][5];
puts("行の要素数を表示");
printf("%1u\n", sizeof stock / sizeof stock[0]);
puts("列の要素数を表示");
printf("%1u\n", sizeof stock[0] / sizeof stock[0][0]);
puts("全体の要素数を表示");
printf("%1u\n", sizeof stock / sizeof stock[0][0]);
puts("各値を表示");
printf("%1u\n", sizeof(stock));
printf("%1u\n", sizeof(stock[0]));
printf("%1u\n", sizeof(stock[0][0]));
return 0;
}
実行結果
array9.exe
行の要素数を表示
3
列の要素数を表示
5
全体の要素数を表示
15
各値を表示
60
20
4
