再び配列とポインタその2
前回の再び配列とポインタでは、配列とポインタについて少しつっこんでみました。
今回は、もう少し掘り下げてみましょう。
前回は、
Int a[4] = {1,2,3,4};
と配列で宣言すると、a は配列a[ ]の先頭アドレスを示し、
a[0] =*a, a[1]=*(a+1)
というように記述できるよ、ということを書きました。
こう書くと、なんだ、配列とポインタ変数はまったく同じもんじゃん、と思いたくなりますが、
厳密にいうと若干違います。
例えば
int a[4];
int *b;
の場合、 *b++, b++はOK. ですが、*a++, a++とするとコンパイルエラーがでるはずです。
あらやだな、何がチガウンジャという声が聞こえてきそうですが、ポインタ変数のb は変数であり
(ポインタ変数だからね)、a は定数だからです。
従って、b=6 はOK. cも整数変数とすれば、
c=a はOK.でも、a=5
は不可です。
更に、もうひとつ。
int a[4];
というように配列を宣言すると、メモリ上にa[0]からa[3]までの領域が確保されます。
int *b;
とポインタ変数を宣言した場合は、b の領域がメモリー上に確保され、*b に値が代入されると
*b のための領域が別途確保されます。すなわち*bの他にポインタ(b)のための領域がメモリ上に
あるということです。
ちょっと、プログラムでホントかどうか確認してみましょう。
ここでは、2次元配列とポインタ配列で比較してみました。
/* Coded by Tsuyoshi Kasai for LSIC */ /* 2次元配列とポインタ配列 */ #include <stdio.h> char data[2][3] = {"Oh\0","K!\0"}; char *dt[2] ={"Oh\0","K!\0"}; main(){ int i; printf("data:%d data[0]:%d data[1]:%d\n",data,data[0],data[1]); for (i=0;i<2;i++) printf("%s",data[i]); printf("\n\ndt:%d dt[0]:%d dt[1]:%d\n",dt,dt[0],dt[1]); for (i=0;i<2;i++) printf("%s",dt[i]); } |
実行結果をみてみましょう
C:\C>nijigen1 data:102 data[0]:102 data[1]:105 OhK! dt:108 dt[0]:112 dt[1]:116 OhK! C:\C> |
配列の方は data = data[0] = data[0][0]のアドレス=文字列”Oh”の先頭アドレス。
一方、ポインタ配列の方は、
dt= dt[0]のアドレス。 dt[0]=*dt[0]のアドレス=文字列”Oh”の先頭アドレス。
違いがわかりましたか。表にしてみましょう
配列では、data も data[0] も実際のデータの先頭(”O”)をさしているだけです。
一方、ポインタ配列では、実際のデータがある領域(アドレス112から)とは別個に、ポインタのポインタ
であるdt,ポインタのdt[0],dt[1]のための領域が確保されているのが、わかると思います。
アドレス | メモリ上のデータ | |
102(data[0][0]) | O | <--data = data[0] =data[0][0]のアドレス |
103(data[0][1]) | h | |
104 | \0 | |
105 | K | |
106 | ! | |
略 | ||
(dt) | 108 | <--dt[0] のポインタ(アドレス) |
108 (dt[0]) | 112 | <--*dt[0]のポインタ(アドレス) |
110 (dt[1]) | 116 | <--*dt[1]のポインタ(アドレス) |
112 | O | |
113 | h | |
114 | \0 | |
116 | K | |
117 | ! | |
このように、配列として宣言した場合と、ポインタ変数として宣言した場合で、若干相違点があります。
(上記のケースは、2次元配列とポインタ配列)
関数の引数として、配列やポインタ変数を渡そうとすると、この相違に注意しなくてはならないことが
発生します。この点については次回の配列とポインタその3でとりあげます。