例えば、3つクラス1組、2組、3組があり、それぞれ25人の学生がいて、それぞれの点数を記憶して、
データを管理したい場合を考えてみます。
このような場合、
2次配列を利用する次の宣言を、過去に紹介しました。
int ten[3][25];
これは、『 int型が[25]個並ぶ領域 』の配列を3個用意する配列で、
次のようなイメージの記憶域が用意されます。
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ このイメージは、□の1つがint型要素で、右側にある程、そのアドレスが大きくなるメモリ空間です
n_kumi に、2組の2を記憶し、
n_gaku に25番目学生の意味で25を記憶し、
これを利用して、その人に100点の数を記憶したい場合、次のように記憶すると決めて使います。
ten[n_kumi-1][n_gaku-1]=100;
つまり、1組で1番目の人は、 ten[0][0] に記憶することになり、
つまり、2組で1番目の人は、 ten[1][0] に記憶(上記□の所です)することになり、
つまり、3組で1番目の人は、 ten[2][0] に記憶することになります。
さてこのような場合、
ポインタの配列で管理する方法があり、その宣言は次のようになります。
int *ten[3];
これは、『 int型がデータを指し示すポインタ 』を3個用意する配列で、次のメモリイメージを作ります。
□□□
管理したいのは、クラス3つで、クラスの情報がある(学生の点数が並ぶ)領域の先頭位置で、管理するための変数です。
よって各要素の型は、(int *)型です。
これに、学生の点数が並ぶ領域の先頭位置を記憶させ、それから使うことになります。
学生の点数が並ぶ領域の先頭位置を記憶予定の要素なので、すぐに使える変数ではありません。
つまり、別途に各クラス分で、学生の点数を記憶する配列を用意する必要があります。
例えば、別途にmemoryの記憶域を作ります。3クラス×25人=75 より、75の要素で用意すればサイズ的に足ります。
int memory[75];
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
そして、この記憶域を、ポインタ配列の各要素へ、例えば次のように設定してから使うことになります。
ten[0]=memory; /* 1組の情報記憶位置の設定 */
ten[1]=memory+25; /* 2組の情報記憶位置の設定 */
ten[2]=memory+50; /* 3組の情報記憶位置の設定 */
このようポインタ要素を設定した後で、やっと使えることになります。
例えば、ten[0]は1組の情報の先頭位置(int*)型を記憶しているので、それに配列演算子を使うことで、各学生のint型データを
アクセスできます。
n_kumi に、2組の2を記憶し、
n_gaku に25番目学生の意味で25を記憶し、
これを利用して、その人に100点の数を記憶したい場合、2次配列の場合と似ている次の操作が可能になります。
ten[n_kumi-1][n_gaku-1]=100;
つまり、1組で1番目の人は、 ten[0][0] に記憶することになり、
つまり、2組で1番目の人は、 ten[1][0] に記憶(上記□の所です)することになり、
つまり、3組で1番目の人は、 ten[2][0] に記憶することになります。
なお、配列名 ten の表現は (int *)型 を指し示すポインタなので、
その型は、 (int **)型 という表現になります。
さてポインタ配列が、2次配列の使い勝手と大きく違うとことは、
各ポインタ要素が指し示すデータ群の個数が自由ということです。
(対応するようにプログラムを書く必要がありますが・・)
例えば、1組の人数が25人、2組が15人、3組が35人とします。
これを、2次配列で行なうと、最も多い、35の配列で配列をつくらなければなりません。
int ten[3][35];/* 必要ないのに、3×35=105個の記憶域が作られます */
ですが、ポインタの配列の方は、3つのクラスを管理することに変りはないので、
tenの配列宣言に変更の必要はありません。
使う前の初期設定を次のように変更すれば使えることになります。
ten[0]=memory; /* 1組の情報記憶位置の設定 */
ten[1]=memory+25; /* 2組の情報記憶位置の設定 */
ten[2]=memory+25+15; /* 3組の情報記憶位置の設定(残りの35が使える) */
これにより、int型75個のmemoryで3クラスを管理できることができます。
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
すでに用意してある記憶域のアドレス表現を使って、次のような初期化が可能です。
int memory[3][25]; int *ten[3] = { memory[0], memory[1], memory[2] }; /* 2次配列の要素(int *)で初期化*/
int memory[75]; int *ten[3] = { memory, memory+25, memory+25+15 }; /* 1次配列の要素位置で初期化*/
文字列定数は、その文字(char)並びの先頭位置を表現する(char *)型なので、 (char *)型を要素とする配列の初期化で使うことができます。
char *a[] = { /* [ ] の中に 3が省略されている */ "ab", "x", "1234" };
この場合、使われている文字列定数が、メモリ空間のどこかに配置されます。
便宜上、次の番地に配置されたとします。
100番地 | 101番地 | 102番地 | 103番地 | 104番地 | 105番地 | 106番地 | 107番地 | 108番地 | 109番地 |
'a' | 'b' | '\0' | 'x' | '\0' | '1' | '2' | '3' | '4' | '\0' |
この場合、aの配列のメモリーイメージは次のようになります。
a[0] | a[1] | a[2] |
100番地 | 103番地 | 105番地 |
なおこのイメージで、a[1][0]
の文字から始まる文字列をputsで表示する場合は、
puts( a[1] )または、puts( & a[1][0] )で、できます。
char型2次配列の文字列による初期化←と、イメージを比較ください。
次のようにポインタ要素を配列にして、それを配列にしたイメージです。
char n[] = "□"; char o[] = "○"; char x[] = "××"; char *a[][3] = { /* [ ] の中に 2が省略されている */ { n,x,n }, { n,o,o }, };