配列の場合とポインタの場合で、考察します。
C
1char p [] = "LOVE"; 2const char* t = "HAPPY"; 3(char* t = "HAPPY"は間違った記述法でしたのでconstを付けました。)
LOVEとHAPPYは文字列定数ですよね・・・??
この2つ、文字列定数の保存先は
「配列の場合はスタック領域に、ポインタの場合はスタティック領域(静的記憶期間)で、ポインタと配列で保存される領域は異なる。」
という理解で正しいですか??
同じ文字列定数でも、配列の場合とポインタの場合で違いがよくわかりません。
「文字列定数に関しては配列の場合は上書きしても構わないが、ポインタの時はダメ!」という理解で正しいでしょうか??
どうして、同じ定数なのに「配列の場合」と「ポインタの場合」で書き換えてはいけないのですか?
(「文字列定数の保存先が異なるから」だと思ったのですが・・・)
以下の実験コードは問題がないんですよね?
C
1const char* t = "HAPPY"; 2t = "aaaa"; 3strcpy(t + 3, "eeee");
これらは何のエラーもなく普通に動いています。
constをつけているということはtの値を書き換えることはできませんよね??
なぜ"aaaa"の先頭アドレスをtに代入することができるのですか??
strcpy()は定数を書き換えているんでしょうか??(その場合、この記述は本来はダメですよね?)
(t+3)のアドレスの位置に"eeee"のアドレスを埋め込んでいるということですか??
どなたか、教えてください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
ベストアンサー
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
の初期値として、左辺を代入します。つまり、この動作は、
- スタックにconstなcharのポインタの領域を確保する。
- その領域に
"HAPPY"
の先頭アドレス値を初期値として入れる。
と言う動作です。x
のためにスタックに確保されるのはポインタ分だけです。そのポインタ分にあらかじめ用意してあった"HAPPY"
を示すアドレスを入れているとなります。なお、Cではchar *
からconst char *
への型変換が発生しますが、constを付けることは暗黙的に可能なため、問題は起きません。
しかし、配列に使う場合は解釈が違います。
まず、配列の場合、次のように書けることは知っているかと思います。
C
1int a[] = {2, 3, 5};
これはサイズ3のintの配列がスタック領域に確保され、それぞれの初期値をもつことがわかると思います。
そして、今回のchar p [] = "LOVE";
は次と同じです。
C
1char p[] = {'L', 'O', 'V', 'E', '\0'};
これはサイズ5のcharの配列がスタック領域に確保され、それぞれの初期値をもつことがわかると思います。
わかりましたか?つまりです。charの配列の初期値として文字列リテラルを渡した場合は、通常の文字列リテラルとして解釈されるのでは無く、初期化するための文字列リテラルとして解釈されると言うことです。同じような文字列リテラルに見えて、その解釈は全く異なります。さらには、次のように書くこともできます。
C
1char p[] = {"LOVE"};
これだと、配列の初期値として使っているというのがわかると思います。そして、この{}
はオプションであり、あってもなくても同じです。逆に言うと、普段は省略した書き方をしているとも言えます。
ということで、そもそも同じように見えて、実は解釈から違うという話がわかったと思います。だから、いろいろと違いが出てくると言うことです。
投稿2017/01/17 10:23
総合スコア21733
0
こんにちは。
char p [] = "LOVE"; は定数ではなく、配列の初期化子(と呼ぶのかな?)です。
http://exlight.net/devel/cpp/string_initialize.html
"LOVE" を指定すると、5バイト(4文字 + 終わりの0x00)の配列が生成されます。
スタック領域につくられるか、ヒープ領域につくられるかは、宣言・定義した位置によります。
サンプルコードを Visual Studio 2013 でコンパイルすると、
エラー 1 error C2664: 'char *strcpy(char *,const char *)' : 引数 1 を 'const char *' から 'char *' へ変換できません。
と失敗しました。このあたり、処理系で違うのですかね...
投稿2017/01/17 08:04
編集2017/01/17 08:08総合スコア728
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
C
1char p[] = "LOVE";
これは、配列pを宣言して"LOVE"という文字列で初期化しています。pはあくまで配列名なのでポインターとしてアドレスを書き換えることは出来ません。。
つまり、
C
1char p[] = "LOVE"; 2const char *t = "HAPPY"; 3*(p + 0) = 'H'; // <--これはできる 4p = t; // <--これは出来ない
ということで、アドレスが指し示す中身を書き換えたり参照したりする場合は、ポインター的な使い方ができますが、アドレスそのものを書き換えることは出来ません。
C
1const char* t = "HAPPY"; 2t = "aaaa"; 3strcpy(t + 3, "eeee");
この場合のtはポインターです。なので、アドレスを書き換えることは出来ます。(constがついているので、アドレスが指し示す中身を書き換えることは出来ません。
strcpy(t + 3, "eeee")は、コンパイラーによってはエラーにはなると思います。(ポインターtがconst char* なのでchar*に変換できないのでエラーになる。ただし、キャストすればその限りではなくなります)
ただし、tが5文字+NULLの領域しか確保されていないため、(t+3)から4文字("eeee")を書いてしまうと、バッファオーバーランを引き起こしてしまうので、以降の動作がどうなるかは保証できません。(領域を越えてのアクセスなので例外が出たり暴走したり何か別のデータを書き換えてしまったりとかすると思います)
ポインター(アドレス)そのものも書き換えたくない場合は、
C
1const char* const t = "HAPPY";
になります。
投稿2017/01/17 08:03
編集2017/01/17 08:07総合スコア3579
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/17 08:20
2017/01/17 08:28
2017/01/17 08:38
2017/01/17 08:41
0
constをつけているということはtの値を書き換えることはできませんよね??
なぜ"aaaa"の先頭アドレスをtに代入することができるのですか??
strcpy()は定数を書き換えているんでしょうか??(その場合、この記述は本来はダメですよね?)
(t+3)のアドレスの位置に"eeee"のアドレスを埋め込んでいるということですか??
const char* はポインタの中身をconstにする宣言です。
ポインタ自体を const にする場合は、 char* const です。
試していないですが、以下のコードは t= "aaaa" でコンパイルエラーになるはずです。
C
1const char* const t = "HAPPY"; 2t = "aaaa";
質問の例にあるコードは ポインタ t を入れ替えているので実行できる、、、けれど確保していない領域に書いているので、メモリを壊していますね。
投稿2017/01/17 07:49
編集2017/01/17 07:50総合スコア1150
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/17 07:54
2017/01/17 07:54
2017/01/17 08:31
2017/01/17 08:33
2017/01/17 08:41
0
char *t = "HAPPY"は、文法エラーでは無いと思います。HAPPYと定義されたアドレスの先頭をtがさしています。プログラム値として定義されているので書き換えは不可です。
配列は、範囲を超えないければ置き換えできます。
c
1#include<stdio.h> 2#include<string.h> 3#include<stdlib.h> 4 5void main(void) 6{ 7 char p[] = "LOVE"; 8 char *t = "HAPPY"; 9 strcpy(p+3, "e"); 10 // strcpy(t+3, "e"); セグメンテーション違反です (コアダンプ) 11 printf("p:%s\n",p); 12 printf("t:%s\n",t); 13}
投稿2017/01/17 08:42
総合スコア4070
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/17 08:45
2017/01/17 08:52
2017/01/17 09:11
2017/01/17 09:16
2017/01/17 09:17
2017/01/17 09:53
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/17 11:19
2017/03/02 04:54