再び配列とポインタその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でとりあげます。