配列の使い方
初期値の代入
配列も、今までの変数と同様に、宣言と同時に初期化できます。
配列の初期化は、次のようにして行います。
{}で囲んだ中に順番に,で区切って数値を並べます。
すべてを指定する必要はないので、数値の個数は、配列の要素数以下にします。
配列の要素数よりも少ない場合、残りにはすべて0が代入されます。
次のプログラムは、配列を初期化して表示する例です。
このプログラムの実行結果は、次の通りになります。
この方法で宣言した場合、要素数を省略できます。
その場合は、指定した数値の個数だけの要素数が確保されます。
次のプログラムは、要素数を省略して配列を宣言する例です。
要素数を指定する手間が省ける上に、数え間違いもなくなるので、
初期値を代入できる場合には省略する方が良いでしょう。
1度に配列に値を代入するのは、宣言の時にしかできません。
たとえば、次のような代入はできません。
これらの書き方は、コンパイルエラーとなってしまいます。
宣言のあとで値を代入したい場合は、面倒でも1つ1つ代入します。
配列の初期化は、次のようにして行います。
配列の初期化
型名 配列名[要素数]={0番の数値,1番の数値,2番の数値,・・・};
{}で囲んだ中に順番に,で区切って数値を並べます。
すべてを指定する必要はないので、数値の個数は、配列の要素数以下にします。
配列の要素数よりも少ない場合、残りにはすべて0が代入されます。
次のプログラムは、配列を初期化して表示する例です。
ソースコード
#include <stdio.h>
int main(void)
{
int array[10] = { 42, 79, 13 };
printf("array[0] = %d\n", array[0]);
printf("array[1] = %d\n", array[1]);
printf("array[2] = %d\n", array[2]);
printf("array[3] = %d\n", array[3]);
printf("array[4] = %d\n", array[4]);
return 0;
}
このプログラムの実行結果は、次の通りになります。
実行結果
array[0]= 42
array[1]= 79
array[2]= 13
array[3]= 0
array[4]= 0
array[1]= 79
array[2]= 13
array[3]= 0
array[4]= 0
この方法で宣言した場合、要素数を省略できます。
その場合は、指定した数値の個数だけの要素数が確保されます。
次のプログラムは、要素数を省略して配列を宣言する例です。
ソースコード
#include <stdio.h>
int main(void)
{
int array[] = { 42, 79, 13 }; /* 要素数が省略されている */
printf("array[0] = %d\n", array[0]);
printf("array[1] = %d\n", array[1]);
printf("array[2] = %d\n", array[2]);
return 0;
}
要素数を指定する手間が省ける上に、数え間違いもなくなるので、
初期値を代入できる場合には省略する方が良いでしょう。
1度に配列に値を代入するのは、宣言の時にしかできません。
たとえば、次のような代入はできません。
できない代入
array = { 42, 79, 13 };
array[10] = { 42, 79, 13 };
これらの書き方は、コンパイルエラーとなってしまいます。
宣言のあとで値を代入したい場合は、面倒でも1つ1つ代入します。
できる代入
array[0] = 42;
array[1] = 79;
array[2] = 13;
全要素の表示
配列のすべての要素を表示するために、for文のループを使うことができます。
この様な使い方ができることこそ、配列の最大の利点です。
次のプログラムは、配列内の要素の数をすべて表示する例です。
このプログラムの実行結果は次の通りになります。
このプログラムでのポイントは、配列の要素番号には変数も使えることです。
変数の値を変化させれば、さまざまな番号の要素に直接アクセスできます。
配列ではない普通の変数では、5つの要素の表示にはprintf関数が5つ必要ですし、
100個表示するには100個必要ですが、配列ならfor文の数値を変えるだけです。
これなら、1万人のデータを表示させるプログラムだって簡単に書けます。
他にも、ループ内の文を変えることで、全要素に同じ値を代入したり、
全要素に代入された値の平均を求めることも簡単にできます。
この様な使い方ができることこそ、配列の最大の利点です。
次のプログラムは、配列内の要素の数をすべて表示する例です。
ソースコード
#include <stdio.h>
int main(void)
{
int array[] = { 42, 79, 13, 75, 19 };
int i;
for (i = 0; i < 5; i++) {
printf("array[%d] = %d\n", i, array[i]);
}
return 0;
}
このプログラムの実行結果は次の通りになります。
実行結果
array[0] = 42
array[1] = 79
array[2] = 13
array[3] = 75
array[4] = 19
array[1] = 79
array[2] = 13
array[3] = 75
array[4] = 19
このプログラムでのポイントは、配列の要素番号には変数も使えることです。
変数の値を変化させれば、さまざまな番号の要素に直接アクセスできます。
配列ではない普通の変数では、5つの要素の表示にはprintf関数が5つ必要ですし、
100個表示するには100個必要ですが、配列ならfor文の数値を変えるだけです。
これなら、1万人のデータを表示させるプログラムだって簡単に書けます。
他にも、ループ内の文を変えることで、全要素に同じ値を代入したり、
全要素に代入された値の平均を求めることも簡単にできます。
要素数を求める
前項では、for文のループを使用して配列の要素をすべて表示しました。
繰り返し回数の5は直接指定しましたが、これでは要素数を数えなくてはなりません。
この方法では、要素数が増えるたびに、for文を書き直す必要があります。
それは面倒くさいので、要素数を自動的に求めて繰り返させることにします。
要素数を求める直接的な方法は用意されていませんが、計算することはできます。
配列全体のサイズを求め、それを要素1つのサイズで割れば要素の数がわかります。
C言語には、変数や配列のサイズを求めるsizeof(サイズオブ)演算子があります。
sizeof演算子は、次のようにして使います。
sizeof演算子には()をつけなくても良いのですが、つけた方が読みやすいため、つけるのが一般的です。
この演算子を使って配列arrayの要素数を計算するには次のようにします。
array[0]としているのは、配列の長さが1であっても、配列0番の要素は必ずあるからです。
array[1]としてしまうと、配列の長さが1のときには、エラーになってしまいます。
次のプログラムは、この方法で前項のプログラムを書き直した例です。
実行結果は、前回と同じになります。
配列の要素数を変えると、自動的にその数だけ表示します。
繰り返し回数の5は直接指定しましたが、これでは要素数を数えなくてはなりません。
この方法では、要素数が増えるたびに、for文を書き直す必要があります。
それは面倒くさいので、要素数を自動的に求めて繰り返させることにします。
要素数を求める直接的な方法は用意されていませんが、計算することはできます。
配列全体のサイズを求め、それを要素1つのサイズで割れば要素の数がわかります。
C言語には、変数や配列のサイズを求めるsizeof(サイズオブ)演算子があります。
sizeof演算子は、次のようにして使います。
sizeof演算子
sizeof(変数や配列名)
sizeof演算子には()をつけなくても良いのですが、つけた方が読みやすいため、つけるのが一般的です。
この演算子を使って配列arrayの要素数を計算するには次のようにします。
配列arrayの要素数を求める
sizeof(array) / sizeof(array[0])
array[0]としているのは、配列の長さが1であっても、配列0番の要素は必ずあるからです。
array[1]としてしまうと、配列の長さが1のときには、エラーになってしまいます。
次のプログラムは、この方法で前項のプログラムを書き直した例です。
ソースコード
#include <stdio.h>
int main(void)
{
int array[] = { 42, 79, 13, 75, 19 };
int i;
for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) {
printf("array[%d] = %d\n", i, array[i]);
}
return 0;
}
実行結果は、前回と同じになります。
配列の要素数を変えると、自動的にその数だけ表示します。
要素数を変数に格納してムダを省く
実は、上記のプログラムにはムダがあります。
なぜならループ1回毎に「sizeof(array) / sizeof(array<0>)」の計算をしているためです。
int array_count = sizeof(array) / sizeof(array<0>);
for (i = 0;i < array_count;i++) {
として、要素数を変数に格納しておくことで、毎回計算するムダを省くことができます。
まあ、今のパソコンはとても高性能なので、あまり大差ないんですけどね・・・
なぜならループ1回毎に「sizeof(array) / sizeof(array<0>)」の計算をしているためです。
int array_count = sizeof(array) / sizeof(array<0>);
for (i = 0;i < array_count;i++) {
として、要素数を変数に格納しておくことで、毎回計算するムダを省くことができます。
まあ、今のパソコンはとても高性能なので、あまり大差ないんですけどね・・・
配列のコピー
ある配列のすべての要素の値を他の配列に代入するにはfor文を使います。
このプログラムの実行結果は次の通りになります。
結果を見ると、array1の値がarray2にコピーされてることがわかります。
しかし、for文を使わなくても、memcpy関数を使うことができます。
なお、memcpy関数を使うには、memory.h ファイルを #include する必要があります。
配列全体のサイズは配列の型や要素数によって変わってくるので、
この関数では、sizeof演算子で得られたサイズを元にします。
配列のすべての要素をコピーするには、sizeof演算子で配列名を指定します。
次のプログラムは、memcpy関数で配列をコピーする例です。
実行結果は、先ほどとまったく同じになります。
ソースコード
#include <stdio.h>
int main(void)
{
int array1[] = { 42, 79, 13, 19, 41 };
int array2[] = { 1, 2, 3, 4, 5 };
int i;
for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
printf("array2[%d] = %d\n", i, array2[i]);
}
/* array1 の全要素を array2 にコピー */
for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
array2[i] = array1[i];
}
for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
printf("array2[%d] = %d\n", i, array2[i]);
}
return 0;
}
このプログラムの実行結果は次の通りになります。
実行結果
array2[0] = 1
array2[1] = 2
array2[2] = 3
array2[3] = 4
array2[4] = 5
array2[0] = 42
array2[1] = 79
array2[2] = 13
array2[3] = 19
array2[4] = 41
array2[1] = 2
array2[2] = 3
array2[3] = 4
array2[4] = 5
array2[0] = 42
array2[1] = 79
array2[2] = 13
array2[3] = 19
array2[4] = 41
結果を見ると、array1の値がarray2にコピーされてることがわかります。
しかし、for文を使わなくても、memcpy関数を使うことができます。
なお、memcpy関数を使うには、memory.h ファイルを #include する必要があります。
memcpy関数
memcpy(コピー先配列名、コピー元配列名、配列全体のサイズ)
配列全体のサイズは配列の型や要素数によって変わってくるので、
この関数では、sizeof演算子で得られたサイズを元にします。
配列のすべての要素をコピーするには、sizeof演算子で配列名を指定します。
次のプログラムは、memcpy関数で配列をコピーする例です。
ソースコード
#include <memory.h>
#include <stdio.h>
int main(void)
{
int array1[] = { 42, 79, 13, 19, 41 };
int array2[] = { 1, 2, 3, 4, 5 };
int i;
for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
printf("array2[%d] = %d\n", i, array2[i]);
}
memcpy(array2, array1, sizeof(array1)); /* array1 の全要素を array2 にコピー */
for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
printf("array2[%d] = %d\n", i, array2[i]);
}
return 0;
}
実行結果は、先ほどとまったく同じになります。
バッファオーバーラン
上記では、コピー元の配列(array1)と、コピー先の配列(array2)は、同じ長さです。
したがって、はみ出したりすることはなく、ぴったりコピーされます。
しかし、困ったことに、memcpy関数は、それぞれの配列の長さを考慮してくれません。
コピー元の配列が、コピー先より長くても、無理やり全部コピーしてしまいます。
その結果として、長い部分がはみ出してコピーされてしまいます。
その結果、無関係の他の変数や配列を上書きして、重大な計算エラーを引き起こします。
これが、悪名高いバグである「バッファオーバーラン」です。
アプリやパソコンが突然止まったりする原因の多くが、このバッファオーバーランです。
まあ、厳密には、はみ出してコピーされただけの場合はバッファオーバーフローですが・・・
C言語は、とてもバグがおきやすい言語です。
ちょっとプログラムを間違えると、すぐにアプリが止まってしまいます。
JavaやC#やRustなどのより新しいプログラミング言語は、
バッファオーバーランがなるべくおこらないように設計されています。
したがって、はみ出したりすることはなく、ぴったりコピーされます。
しかし、困ったことに、memcpy関数は、それぞれの配列の長さを考慮してくれません。
コピー元の配列が、コピー先より長くても、無理やり全部コピーしてしまいます。
その結果として、長い部分がはみ出してコピーされてしまいます。
その結果、無関係の他の変数や配列を上書きして、重大な計算エラーを引き起こします。
これが、悪名高いバグである「バッファオーバーラン」です。
アプリやパソコンが突然止まったりする原因の多くが、このバッファオーバーランです。
まあ、厳密には、はみ出してコピーされただけの場合はバッファオーバーフローですが・・・
C言語は、とてもバグがおきやすい言語です。
ちょっとプログラムを間違えると、すぐにアプリが止まってしまいます。
JavaやC#やRustなどのより新しいプログラミング言語は、
バッファオーバーランがなるべくおこらないように設計されています。
本サイトについて
苦しんで覚えるC言語(苦C)はC言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。




