4 配列

同じ型のデータが大量にある場合,配列を使うと便利である.配列名と自然数の添え字に よりデータが指定できるので,大量のデータでも容易にアクセスできる.この配列とメモ リー及びポインターの関係を述べる.

ここでの話で特に重要なことは,配列の添え字から,データが格納されているアドレスを 割り出す方法である.

4.1 一次元配列

C言語では,配列名はデータの先頭を表すポインターのように動作する.以前学習したよ うに,ポインターの値に1加算すると,次のデータを示すようになる.したがって,配列 名に1加算すると,次の配列の値を示すポインターになる.これについては,具体例を示 した方が分かりやすいだろう.リスト5を使って説明する. -4pt
4行
整数型のポインターpと要素数3の整数型の配列a,整数型の変数 iの宣言
10行
配列aの先頭アドレスをポインターpへ代入
12行
ポインターpのアドレスと,そこに格納されているアドレスを表示
15行
配列のアドレスと値,aをポインターと考えた場合のアドレスと値,ポ インターpのアドレスと値の表示

   1 #include <stdio.h>
   2 
   3 int main(void){
   4   int *p, a[3], i;
   5   
   6   a[0]=11;
   7   a[1]=22;
   8   a[2]=33;
   9 
  10   p=a;
  11 
  12   printf("%p %p\n", &p, p);
  13 
  14   for(i=0;i<3; i++){
  15     printf("%p %d %p %d %p %d\n", &(a[i]), a[i], a+i, *(a+i), p+i, *(p+i));
  16   }
  17 
  18   return 0;
  19 }
\fbox{実行結果}
0xbffff69c 0xbffff680
0xbffff680 11 0xbffff680 11 0xbffff680 11
0xbffff684 22 0xbffff684 22 0xbffff684 22
0xbffff688 33 0xbffff688 33 0xbffff688 33

このプログラムの実行直後のメモリーの様子を図1に示す.このメ モリーの様子と実行結果から,次に示す2つのことが分かるだろう.

まずは,配列名は配列の先頭アドレスを示すポインターのように動作する.したがって, リスト5の10行目のように,左辺値として配列名を指定して,それを ポインターに代入することができる.

次に分かることは,配列への要素のアクセスは,ポインターを使って表現できる.すなわ ち,a[i]は,*(a+i)と同じであることがわかる.事実,コンパイラーはこのよ うにしてメモりーにアクセスするように機械語に変換するのである.

図 1: プログラム実行後のメモリーの様子
\includegraphics[keepaspectratio, scale=1.0]{figure/array_1D.eps}

4.2 多次元配列

C言語の多次元配列について正確に述べようとすると,ここでの説明よりも,さらにもっ と込み入った話がある.ますます混乱する者が多くなりそうなので,ここではコンパイラー の動作を考慮した細かい説明は避ける.興味のある者は,自分で学習せよ.

これまでの話で,一元のポインターは分かった.2次元以上はどうなっているのだろうか?. 同じようにプログラムを作成して調べてみるのが良いだろう.ここでは,配列の添え字か らどのようにしてメモりーのアドレスの導出方法に興味がある.そのために,リスト 6の2次元の配列を使ったプログラムを考える2

このプログラムの実行結果と図2のメモリーの様子から,a[i][j] は配列名の先頭アドレスa3*i+j加算したアドレスになることが分かる.

さらに,ここでも配列はポインターを用いて,

a[i][j] $ \quad\rightarrow\quad$*(a+i)[j] $ \quad\rightarrow\quad$*(*(a+i)+j)
となっていることが分かる.ここのところは分からなくても良い.
   1 #include <stdio.h>
   2 
   3 int main(void){
   4   int a[2][3], i,j;
   5   int *p;
   6 
   7   p=a;
   8 
   9   printf("pointer p address %p  value %p\n", &p, p);
  10   
  11   a[0][0]=0;   a[0][1]=1;   a[0][2]=2;
  12   a[1][0]=10;  a[1][1]=11;  a[1][2]=12;
  13 
  14   for(i=0; i<2; i++){
  15     for(j=0; j<3; j++){
  16       printf("%p %d %p %d %p %d\n",
  17 	     &(a[i][j]), a[i][j], p+3*i+j, *(p+3*i+j), *(a+i)+j, *(*(a+i)+j));
  18 
  19     }
  20   }
  21 
  22   return 0;
  23 }
\fbox{実行結果}
pointer p address 0xbffff674  value 0xbffff680
0xbffff680 0 0xbffff680 0
0xbffff684 1 0xbffff684 1
0xbffff688 2 0xbffff688 2
0xbffff68c 10 0xbffff68c 10
0xbffff690 11 0xbffff690 11
0xbffff694 12 0xbffff694 12
図 2: 2次元配列を使ったプログラムの実行後のメモリーの様子
\includegraphics[keepaspectratio, scale=1.0]{figure/array_2D.eps}



ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年2月16日


no counter