配列は、データがメモリ上に順に並んでいるので、ポインタと相性がいい。前のエントリでさらっと書いたけど、配列名を添字を付けずに使うと、先頭を指すポインタを返す。
int a[10]; int *p; p = a;
配列の要素にポインタでアクセス
配列の各要素には、添字を使ってアクセスするほかにポインタを使ってもアクセスできる。
#include int main(void) { int a[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; int *p; p = a; printf("%d %d %d\n", a[0], a[1], a[2]); /* access with index */ printf("%d %d %d\n", *p, *(p + 1), *(p + 2)); /* access with pointer */ return 0; }
takatoh@nightschool $ ./sample_6_3a 10 20 30 10 20 30
2次元配列
2次元配列の場合は、ポインタを型キャストしてやる必要がある。これはよくわからないな。次のような float 型の2次元配列の要素にアクセスするには、float 型のポインタでいいんじゃなかろうか。
#include int main(void) { float balance[5][5] = { { 1.0, 2.0, 3.0, 4.0, 5.0}, { 6.0, 7.0, 8.0, 9.0, 10.0}, {11.0, 12.0, 13.0, 14.0, 15.0}, {16.0, 17.0, 18.0, 19.0, 20.0}, {21.0, 22.0, 23.0, 24.0, 25.0} }; float *p; p = balance; printf("%f\n", *p); printf("%f\n", *(p + 1)); return 0; }
これを試してみると:
takatoh@nightschool $ gcc sample_6_3b.c -o sample_6_3b sample_6_3b.c: In function ‘main’: sample_6_3b.c:15:3: warning: assignment from incompatible pointer type [enabled by default] p = balance; ^ takatoh@nightschool $ ./sample_6_3b 1.000000 2.000000
出力結果は予想どおりだけど、コンパイル時に warning が出ている。互換性のないポインタを代入している、ということらしい。でもちゃんと動いてるんだけど…。
ポインタへの代入を次のように直すと warning は出なくなった。
#include int main(void) { float balance[5][5] = { { 1.0, 2.0, 3.0, 4.0, 5.0}, { 6.0, 7.0, 8.0, 9.0, 10.0}, {11.0, 12.0, 13.0, 14.0, 15.0}, {16.0, 17.0, 18.0, 19.0, 20.0}, {21.0, 22.0, 23.0, 24.0, 25.0} }; float *p; p = (float *) balance; printf("%f\n", *p); printf("%f\n", *(p + 1)); return 0; }
takatoh@nightschool $ gcc sample_6_3c.c -o sample_6_3c takatoh@nightschool $ ./sample_6_3c 1.000000 2.000000
ポインタの添字
ポインタが配列を指しているときに限って、ポインタに添字をつけることができる。
#include int main(void) { char str[] = "Pointer is interesting."; char *p; int i; p = str; for (i = 0; p[i]; i++) { printf("%c", p[i]); } printf("\n"); return 0; }
takatoh@nightschool $ ./sample_6_3d Pointer is interesting.
配列のアドレスを使う
最初に書いたように、配列名を添字を付けずに書くとその配列の先頭を指すポインタを返す。なのでこれを普通のポインタの代わりに使うこともできる。
#include int main(void) { char str[80]; *(str + 3) = 'c'; printf("%c\n", *(str + 3)); return 0; }
takatoh@nightschool $ ./sample_6_3e c
[追記]
コメントをもらった。配列名を添字なしで使ったときにポインタのように振る舞うのは、暗黙の型変換が行われるからだそう。配列名の型自体はあくまで配列だと。
ふーむ、なるほど。でも、参照するときに限って言えば、暗黙の型変換のおかげで実質的にポインタとして(というかポインタの代わりに)使えるってことでいいのかな。
配列名はあくまで配列を表します。 int a[10]; と定義したとき a の型は int[10] であって、それを int* 型の変数に代入できるのは暗黙の型変換が働いているからです。
二次元の配列になると実質的には問題なくとも型の辻褄が合わなくなるので警告されることになります。 キャストが不恰好に感じるのであれば p = balance[0]; という風にするか p = &balance[0][0]; とする方法もあります。