配列のポインタというかポインタのポインタというかポインタでハマったので忘れないうちに簡単にまとめてみます。
配列のポインタとは
ポインタの配列ではなく配列のポインタ。
例えば
char hoge[] = "fuga";
と宣言します。
すると配列のポインタは&hoge
です。
配列と配列のポインタ
ここで配列には配列のポインタが存在するので&hoge
、すなわち配列のポインタを参照することができます。
ここでhoge
と&hoge
のポインタを見てみると
printf("hoge = %p\n", hoge);
//=> hoge = 0061FF2B
printf("&hoge = %p\n", &hoge);
//=> &hoge = 0061FF2B
このようになります。
結局同じアドレスを指しています。
これは配列の先頭アドレスになるので結局配列のポインタも配列の文字列の先頭アドレスを指します。
じゃあ何が違うんだ!というと型が違います。
hoge
はchar*
型ですが&hoge
は~~char**
型になります~~(訂正:追記参照)。
つまり型違いで同じアドレスを指しているわけです。
追記(1/25)
&hoge
はchar**
型ではありません。
配列のポインタ型であり、char* []
型になります。
shiracamusさんコメントありがとうございました。
ポインタのポインタと配列のポインタ
私が陥った罠はこれです。
関数間で配列を受け渡すとき、配列のアドレスを仮引数で宣言したポインタへ受け渡します。
hoge
をpiyo
の引数として渡すと、
void piyo(char* foo)
{
printf("foo = %p\n", foo);
//=> foo = 0061FF2B
printf("foo = %p\n", &foo);
//=> foo = 0061FF10
}
このようになります。
配列のポインタと思い込んで&foo
を参照したところ全く違うアドレスを参照してしまいました。
つまりこのときのfoo
は配列ではなくポインタであるため、&foo
は配列hoge
のポインタではなく配列hoge
のアドレスを持ったポインタfoo
のポインタになります。
つまり配列とはまったく別物で、変数foo
のアドレスを指しています。
ポインタを理解していないせいでここでハマりました…
ここで&foo
はchar**
型なので例えばchar* pに
アドレスを渡すときは&foo
をchar*
型にキャストしなければ型が競合します。
ポインタとはアドレスを保持することのできる変数、と理解しておくとこのようなミスがなくなるのではと思います。
考察
ここからは考察になりますが、ポインタとはアドレスを保持することのできる変数、であり、*p
のようなポインタの指す変数の中身を参照するときの挙動としては、保持している先のアドレスを参照し、そこにある値を見ているのではと思います。というかそれしか説明つかない…
おわりに
仮引数で宣言したポインタに配列を渡すときはアドレスが渡されていて配列は渡されているわけではないという理解が大事だったんだなぁと感じました。