/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ int x[16];
/*06*/ int n;
/*07*/ int total = 0;
/*08*/
/*09*/ for(n = 0; n < 16; n++){
/*10*/ x[n] = n*n;
/*11*/ total += x[n];
/*12*/ }
/*13*/
/*14*/ for(n = 0; n < 16; n++){
/*15*/ printf("x[%d] = %d\n", n, x[n]);
/*16*/ }
/*17*/
/*18*/ printf("合計は %d です。\n", total);
/*19*/
/*20*/ return 0;
/*21*/ }
配列を宣言しています。
int x[n];
int x[4]; と宣言すると、メモリ上には |<------ x[0] ------->|<------ x[1] ------->| ----------------------------------------------------- ...| 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | ----------------------------------------------------- |<--- int (4byte) -</C>| |<------ x[2] ------->|<------ x[3] ------->| ----------------------------------------------------- | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 |... ----------------------------------------------------- のように確保されます。
さて、初期化の方法なのですがいくつかあります。宣言と同時に初期化する時は
int x[5] = {12,15,14,47,98}
x[0] = 12; x[1] = 15; x[2] = 14; x[3] = 47; x[4] = 98;
for(n = 0; n < 16; n++){ /*2:*/
x[n] = n*n;
total += x[n];
}
次は多次元配列についてです。添字をいくつも書いたものを
/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ int x[3][5];
/*06*/ int i,j;
/*07*/
/*08*/ for(i = 0; i < 3; i++){
/*09*/ for(j = 0; j < 5; j++){
/*10*/ x[i][j] = i + j;
/*11*/ }
/*12*/ }
/*13*/
/*14*/ for(i = 0; i < 3; i++){
/*15*/ for(j = 0; j < 5; j++){
/*16*/ printf("x[%d][%d] = %d\n", i, j,x[i][j]);
/*17*/ }
/*18*/ }
/*19*/
/*20*/ return 0;
/*21*/ }
このように添字を2つ以上使って配列を宣言するとどうなるでしょう?これを理解するためには縦が3、横が5の表を考えるとよいでしょう.。
--------------------------------------------------- | x[0][0] | x[0][1] | x[0][2] | x[0][3] | x[0][4] | |---------|---------|---------|---------|---------| | x[1][0] | x[1][1] | x[1][2] | x[1][3] | x[1][4] | |---------|---------|---------|---------|---------| | x[2][0] | x[2][1] | x[2][2] | x[2][3] | x[2][4] | ---------------------------------------------------
今までは直線(1次元)的なイメージだった配列が平面(2次元)的なイメージになるわけです。そして、それぞれについてはやはり普通の変数同様に扱えますし、添字に数値変数を使うことも出来ます。もちろん「int x[15];」として、15個の箱を作ってもかまいませんが場合によってはグループ分けしたい時もあるでしょう。そのような時にはこういった使い方をすると便利かもしれません。
ですが、直線的なメモリ上ではどのように確保されるのでしょうか?こういった場合、まず一番右の添字から考えていきます。つまり、上の表を「上段」「中段」「下段」の順に横に並べた感じで、x[0][0]~x[0][4],x[1][0]~x[1][4],x[2][0]~x[2][4] という順番で連続的に確保されます。整数が繰り上がっていくのといっしょですね。
また、初期化をする場合、08行目のように添字に数値変数を使って for 文により初期化する場合がほとんどかもしれませんが、宣言時に初期化することもできます。その場合、
int x[2][3] = {{3,4,6},{2,7,5}};
さて、「多次元配列」といったからには添字を3つ、4つ、5つ、・・・、とすることもできます。その場合、例えば3次元配列なら「int x[7][3][10];」のように宣言し、上の表は立体(3次元)になる感じです。メモリ上の確保も2次元と同様一番右の添え字から考えていき、整数が繰り上がっていく感じの順に確保されます。
では、これ以上の次元はあまり使わないでしょうが、4次元は上の表をどう考えるのでしょうか?「立体に時間軸を加えたものを想像すればいいのです。」・・・と言われても「はぁ?」ですよね。それに、「5次元は?」「6次元は?」と聞かれたら、それこそ分からないですよね。
実は、私はこの「次元」という考え方をあまりいいものではないと思っています。それよりも、Windows の「エクスプローラ」を想像してみてください。「フォルダ(ディレクトリ)」によって階層構造で管理されています。これをイメージすると大変分かりやすくなると思います。.を見てください。x[3][2][3] というドライブがあって階層的なフォルダによって管理されているように見えませんか?しかも、メモリ上には、上からこの順番どおりに確保されていきます。ちなみに、右側に書かれた数字は添字の部分だけをつなげて書いたものですが、まさに「整数の繰り上がり」といっしょですね。
x[3][2][3]
|- x[0][][]
| |- x[0][0][]
| | |- x[0][0][0] 000
| | |- x[0][0][1] 001
| | |- x[0][0][2] 002
| |
| |- x[0][1][]
| |- x[0][1][0] 010
| |- x[0][1][1] 011
| |- x[0][1][2] 012
|
|- x[1][][]
| |- x[1][0][]
| | |- x[1][0][0] 100
| | |- x[1][0][1] 101
| | |- x[1][0][2] 102
| |
| |- x[1][1][]
| |- x[1][1][0] 110
| |- x[1][1][1] 111
| |- x[1][1][2] 112
|
|- x[2][][]
|- x[2][0][]
| |- x[2][0][0] 200
| |- x[2][0][1] 201
| |- x[2][0][2] 202
|
|- x[2][1][]
|- x[2][1][0] 210
|- x[2][1][1] 211
|- x[2][1][2] 212
この考え方からすると「多次元配列」ではなく「多階層配列」と言ったところでしょうか。そもそも人間は、3次元空間でさえイメージするのが大変なんですから、「多次元」で理解するのには限界があります。でも、配列を「階層」でイメージすれば、どんなに添字が増えてもその分だけ階層を深くしてイメージすればいいわけで、「~次元」なんて考えるよりずっと分かりやすいのではないかと思います。もっとも、こんな複雑怪奇な配列を使うことはほとんど無いことでしょうが。
最大で 20 個の整数データを入力し、それらの総和を求めてください。
最大で 20 個の整数データを入力し、それを小さい順に並べ替えてください。
ヒント:Ans06-3-2を参考にしてみてください。
#include <stdio.h>
main()
{
int wa, data[20];
int i, n;
printf("データ数 -> ");
scanf("%d", &n);
for(i = 0; i < n; i++){
printf("data[%d] -> ", i);
scanf("%d", &data[i]);
}
wa = 0;
for(i = 0; i < n; i++){
wa += data[i];
}
printf("総和は %d です。\n", wa);
return 0;
}
#include <stdio.h>
main()
{
/* 次のような配列、変数を用意する
data[20] 最大20個のデータが入る配列
i, j ループカウント用の変数
n データ数を入れておく
tmp 二つのデータを入れ替える時の退避変数
*/
int data[20];
int i, j, n, tmp;
printf("データ数 -> ");
scanf("%d", &n);
for(i = 0; i < n; i++){
printf("data[%d] -> ", i);
scanf("%d", &data[i]);
}
/* 何セット目かをカウントするループ */
for(i = 0; i < n; i++){
/* 隣同士を比較するループ。例えば三つのデータを比較する時を
考えてみればデータの比較回数は 2 回で 1 セットが終わる(n 個の
データなら n - 1 回の比較で 1 セットが終わるということ)。
さらに、 1 セット終わるとその時点で最後の配列には最大の値が
入っているため、この最後部分は比較する必要がない。すなわち、
n セット目では最後部分の n 回分は比較する必要がない。したがって、
各セットでの比較回数は
[データ数(n)] - [セットカウント(i)] - 1
ということになる */
for(j = 0; j < n - i - 1; j++){
if(data[j] > data[j + 1]){
tmp = data[j];
data[j] = data[j + 1];
data[j + 1] = tmp;
}
}
}
printf("並べ替えました。\n");
for(i = 0; i < n; i++){
printf("data[%d] -> %d\n", i, data[i]);
}
return 0;
}