添字演算子と間接参照演算子の振る舞い
添字演算子[ ](ブラケット)と間接参照演算子*(アスタリスク)の振る舞いで迷わないために少しまとめておこうかと思います。
宣言時
char *foo = "abc";
char bar[ ] = "abc";
では確保するRAM(もしくはスタック)が異なります。間接参照演算子を使って初期化した場合はROMに確保された文字列リテラル”abc”分RAM(もしくはスタック)を確保する訳ではありません、あくまで確保するのはアドレスの入れられる箱fooのみです。そこにROMにある”abc"の先頭アドレスを格納するという意味になりますので
*(foo + 1 ) = 'j';
といった代入ができません。隠れconstみたいなものになってしまうので宣言時にconstをつけておくと良いかと思います。
const char *foo = "abc";
配列の意味でポインタを使った場合によくある間違いとして、
char *foo;
itoa(100, foo, 10);
といった様なプログラムを書いてしまうのですが、これではメモリがアロケートされていないので、不定なアドレスに対して書き込みを行う事になります。
一方、添字演算子を使って宣言した場合はROMに確保された"abc"を、添字分確保したRAM(もしくはスタック)にコピーします。ただしスコープ内ではRAM(もしくはスタック)に特定のアドレスを確保しますので、
bar = foo;
といったアドレスの代入はできません。これは通常の変数宣言した場合に
char baz = 0x31;
&baz = foo;
とできないのと同じです。なお配列であれば
char bar[ 10 ];
itoa(100, bar, 10);
が正しいプログラムになります。
参照時
*( foo + 1 );
bar[1];
はまったく同じです。なので相互に交換した記載方法が許されます。ここで *(bar + 1); でも *(1 + bar );でも同じなので、1[bar] ;と書いても同じとなります。参照時の添字演算子は間接参照演算子と加算演算子の複合した演算子という事になります。
ところでプログラム上に書かれた"abc"はその先頭アドレスを表しますので次のような記述が可能です。
char baz;
baz = "abc"[1];
bazにはbが格納されます。もちろん 1["abc"];とも書けます。
まとめ
・宣言時は間接参照演算子と添字演算子で確保するRAM(もしくはスタック)が異なる。(添字演算子の振る舞いの方がより直観的かと思います。間接参照演算子の宣言は参照と異なりポインタであることを明示する為の記号でしかありません。)
・参照時は 添字演算子=間接参照演算子 + 加算演算子 となる。
演算子の振る舞いが宣言時と参照時で異なる場合があることを良く認識しておくのはとても大切です。