配列のポインタというかポインタのポインタというかポインタでハマったので忘れないうちに簡単にまとめてみます。
配列のポインタとは
ポインタの配列ではなく配列のポインタ。
例えば
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のようなポインタの指す変数の中身を参照するときの挙動としては、保持している先のアドレスを参照し、そこにある値を見ているのではと思います。というかそれしか説明つかない…
おわりに
仮引数で宣言したポインタに配列を渡すときはアドレスが渡されていて配列は渡されているわけではないという理解が大事だったんだなぁと感じました。