- ベストアンサー
配列やポインタに文字列を設定することについて
◎1------------------------- #include<stdio.h> int main(void) { char ss[80]; scanf("%s",ss); printf("%s\n",ss); return 0; } ---------------------------- ◎2--------------------------- #include<stdio.h> int main(void) { char *ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- ◎3---------------------- #include<stdio.h> int main(void) { char *ss; ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- 以上3つプログラムで疑問をいだいたのですが、 まず◎1で、これは例えば、 cahr ss[80]="abc"; のように配列ssに文字列"abc"そのものを入れているのか、 char *ss="xyz"; のようにまず"xyz"という文字列をメモリ上のどこかに設定し、その先頭番地をssに代入しているのか、どちらの考えでいいのかわかりません。 次に、◎2、3ではどちらも正常に実行できたのですが、特に◎3で「ss="abcde";」と記述していますが、ssにはアドレスを代入するという認識かあるのですが、文字列定数を代入しても問題ないのか?という疑問があります。 教えていただけたら嬉しいです。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
そうですね。 配列の初期化とポインタの初期化でコンパイラの動作が違うことを理解すれば良いかなと思います。 char ss[80]="abc"; は、ssはcharで80バイトの領域を確保して、そこに値"abc"を初期値として設定します。 char *ss="xyz"; は、ssと言う名前のcharポインタを宣言して、"xyz"の値が入ったメモリ領域を別に確保。その"xyz"の領域のアドレスをssポインタに初期値として設定します。 >「ss="abcde";」と記述していますが、ssにはアドレスを代入するという認識かあるのですが、文字列定数を代入しても問題ないのか?という疑問があります。 基本的に文字列定数はポインタとして扱われますが、配列の初期化時が特別なだけです。 なので、ss="abcde";」も"abcde"という値の入ったメモリ領域のアドレスをssポインタに代入しているだけです。
その他の回答 (3)
- Tacosan
- ベストアンサー率23% (3656/15482)
微妙に C と C++ で相違がある部分 (の 1つ) ですがここでは簡単のため C だけ触れます. 文字列リテラルは「文字配列の初期値」として使う場合と「その他」とで扱いが違っています. 文字列配列を初期化するときには「その配列に入れるべき値の列」を指定しているとみなされ, 実際には #2 に書かれているように解釈されます. 一方, その他の場合は, 実は「文字の無名配列」として扱われます. つまり, char *ss = "xyz"; というのは const char anonymous[] = "xyz"; char *ss = anonymous; と (配列名 anonymous を使うことを除いて) 同じように解釈されます. 規格としては「文字列リテラルを変更してはいけない」ことになっていて, 例えば上の文に続いて *ss = 'a'; とかやってはいけないことになっています. 細かいことですが, C においては確かに 'a' などが 1バイトであるとは限らない (なぜなら 'a' の型は int だから) ですが, "abc" は常に 4バイトです>#3. 実際, C99 で #include <stdio.h> int main() { printf("%zu\n", sizeof "abc"); return 0; } は「4」と表示するはずです. あと, 蛇足ですが char ss[3] = "abc"; が許されます. この場合, 本来なら最後にあるべき '\0' が消滅するので何も考えずに printf("%s\n", ss); とかやると困ったことになり得ます.
お礼
ご回答ありがとうございます。 >文字列リテラルは「文字配列の初期値」として使う場合と「その他」とで扱 >いが違っています. >文字列配列を初期化するときには「その配列に入れるべき値の列」を指定し >ているとみなされ, 実際には #2 に書かれているように解釈されます. >一方, その他の場合は, 実は「文字の無名配列」として扱われます. すいません。プログラミング初心者ということで全てが理解できませんでしたが、上記の内容理解できました! ありがとうございます。
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
追記。 現在のC言語の仕様では「配列の初期化リストに列記した初期値が、配列の要素数に足りない場合は、足りない部分は0で埋められる」となっている。 なので、 >char ss[80] = "abc"; >は >char ss[80] = { 'a' , 'b' , 'c' , '\0', 0 , 0 , …(76個分)… 0 }; >に等しい。 は「現在のC言語の仕様」にしか当てはまらない。 なお、上記の「足りない部分は0で埋められる」は、初期化リストの「0 , 0 , …(76個分)… 0」に相当する。 また「"abc"」は3文字のように見えるが、実体は「'a' , 'b' , 'c' , '\0'」の4文字なので「使用するメモリは4文字分」になる(「4バイト分」ではない事に注意。'a'や'c'や'\0'が1バイトとは限らないから「4文字分」としか言えない) この仕様が制定されるより前の古いCコンパイラでは「足りない部分は、不定になっているかも知れない」ので注意する事。 また「組み込み用の8ビットマイコン用のコンパイラ」などは、プログラムをコンパクトにしなければROMに収まらないので、このような「足りない部分のゼロ初期化を、ワザと行わない」と言う場合もあるので注意する事。
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
◎1 char ss[80] = "abc"; は char ss[80] = { 'a' , 'b' , 'c' , '\0', 0 , 0 , …(76個分)… 0 }; に等しい。 と言うか、本当は char ss[80] = { 'a' , 'b' , 'c' , '\0', 0 , 0 , …(76個分)… 0 }; と書かないとならない。 でも「要素を1つづつ、いっぱい書くのは面倒」なので「特別に、charの配列の初期化の場合だけの、特例中の特例の処置」として char ss[80] = "abc"; と書くのを許す事にした。 なので、今後 char ss[80] = "abc"; とか char ss[] = "abc"; って書いてあるのを見かけたら char ss[80] = { 'a' , 'b' , 'c' , '\0' , 0 , 0 , …(76個分)… 0 }; とか char ss[] = { 'a' , 'b' , 'c' , '\0' }; って書いているのと同じと思う事。 なお、ssは「文字列そのものの入れ物」となる。 なので ss[0]、ss[1] は「0番目の要素」「1番目の要素」となる。 ------------------ ◎2と3 char *ss="abcde"; と char *ss; ss="abcde"; は、まったく同じ意味。 ssは「アドレスの入れ物だけ」であって「中身の文字列はどっか知らないトコにある」って事。 なので ss[0]、ss[1] は「ssが指すアドレスから数えて0番目のメモリの中身」「ssが指すアドレスから数えて1番目のメモリの中身」となる。 参照や代入時の結果は同じだけど◎1の場合の ss[0]、ss[1] の場合とは「全然、意味が違う」と言う事を覚えておく事。
お礼
ご回答ありがとうございます。 ◎1は配列に、入力した文字列がそのまま入るということですね。 詳しく説明ありがとうございます。 それに対して、◎2、3は文字列の先頭アドレスが代入されるということですね。 ◎1と◎2、3では全然意味が違うということをしっかり頭に入れておきます!
お礼
ご回答ありがとうございます。 >基本的に文字列定数はポインタとして扱われますが、配列の初期化時が特別 >なだけです。 >なので、「ss="abcde";」も"abcde"という値の入ったメモリ領域のアドレス >をssポインタに代入しているだけです。 上記の回答、かなり納得できました! しっかり頭に入れておきます。