質問: "aaaa"; strcpy(t + 3, "eeee"); ``` これらは何のエラーもなく普通に動いています。 constをつけているということはtの値を書き換えることはできませんよね?? なぜ"aaaa"の先頭アドレスをtに代入することができるのですか?? strcpy()は定数を書き換えているんでしょうか??(その場合、この記述は本来はダメですよね?) (t+3)のアドレスの位置に"eeee"のアドレスを埋め込んでいるということですか?? どなたか、教えてください。
回答: CとC++で微妙に文字列リテラルの扱い方が違いますので、その点も含めて説明します。 まず、文字列リテラル`"abc"`と書いた場合、 'a' 'b' 'c' '\0' というcharの配列です。型は、Cでは`char *`、C++では`const char *`になります。この配列はプログラムの初期化時に用意されるため、`main`が始まる前に、静的領域にあらかじめ書かれています。そして、リテラル自身はその先頭のアドレス値になります。 なお、C++はconstがあるため(const_castをしないと)書き換えようとしてもコンパイルエラーが発生しますが、Cはconstがないため、書き換えようとするしてもコンパイルエラーには**なりません**。しかし、このような文字列リテラルによって作成される配列への書き換えは**動作未定義**となっているため、実行時にどうなるかは**わかりません**。 以上を踏まえて、`const char* t = "HAPPY";`を見ます。まず、mainを始める前に、`"HAPPY"`がメモリ上のどこかに 'H' 'A' 'P' 'P' 'Y' '\0' というcharの配列で用意されます。`"HAPPY"`自体はこの配列の先頭のアドレス値です。そして、`const char *t`はconstなcharのポインタの領域をスタックに確保した`x`であり、その`x`の初期値として、左辺を代入します。つまり、この動作は、 1. スタックにconstなcharのポインタの領域を確保する。 2. その領域に`"HAPPY"`の先頭アドレス値を初期値として入れる。 と言う動作です。`x`のためにスタックに確保されるのはポインタ分だけです。そのポインタ分にあらかじめ用意してあった`"HAPPY"`を示すアドレスを入れているとなります。なお、Cでは`char *`から`const char *`への型変換が発生しますが、constを付けることは暗黙的に可能なため、問題は起きません。 しかし、**配列に使う場合は解釈が違います**。 まず、配列の場合、次のように書けることは知っているかと思います。 ``` int a[] = {2, 3, 5}; ``` これはサイズ3のintの配列がスタック領域に確保され、それぞれの初期値をもつことがわかると思います。 そして、今回の`char p [] = "LOVE";`は次と**同じ**です。 ``` char p[] = {'L', 'O', 'V', 'E', '\0'}; ``` これはサイズ5のcharの配列がスタック領域に確保され、それぞれの初期値をもつことがわかると思います。 わかりましたか?つまりです。charの配列の初期値として文字列リテラルを渡した場合は、通常の文字列リテラルとして解釈されるのでは無く、**初期化するための文字列リテラルとして解釈される**と言うことです。同じような文字列リテラルに見えて、その解釈は全く異なります。さらには、次のように書くこともできます。 ``` char p[] = {"LOVE"}; ``` これだと、配列の初期値として使っているというのがわかると思います。そして、この`{}`はオプションであり、あってもなくても**同じ**です。逆に言うと、普段は省略した書き方をしているとも言えます。 ということで、そもそも同じように見えて、実は解釈から違うという話がわかったと思います。だから、いろいろと違いが出てくると言うことです。