- ベストアンサー
C言語の基礎的な質問---文字配列の初期化
C言語の配列の初期化に関する質問です。 もし規格によって回答が異なる場合は、ANSIのCということにしてください。 関数の中に、 char str[ ]="ABC"; (イ) という宣言があるとします。(staticは付きません。) これは、 char str[ ]={'A', 'B', 'C', '\0'}; (ロ) と全く同じ意味でしょうか。 似て非なるものに char *str="ABC"; (ハ) というものがあります。この場合は、 strとは違うところに"ABC"('C'の次には'\0'があります。)という領域が確保されていて、 その先頭アドレスでstrが初期化されるのですよね。 (イ)(ロ)(ハ)のいずれの場合も関数の中に書かれているとすれば、 いずれもstrは自動変数で、関数実行時にstrの領域が確保されますよね。 (イ)は配列strの領域が確保されるときに、 配列strとは別のところにある"ABC"という領域の内容を、コピーして設定する、 ということでしょうか。 (ロ)は、配列の領域確保時にstr[0]を'A'で、str[1]を'B'で、str[2]を'C'で、str[3]を'\0'で、初期化する、 ということで、 配列とは別のところには"ABC"という領域はない、 という考えでよろしいでしょうか。 もしそうだとしたら、配列とは別のところに"ABC"という領域があるかどうかという点で(イ)と(ロ)は異なることになりますが、そう考えてよろしいのでしょうか。 それとも、そういうことは処理系に依存することなんでしょうか。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
>この記述は、配列とは別のところに初期値で埋まった領域(質問でいう"ABC")がある、という意味に思えてしまいます。 実行のする度に値が設定されるわけですから、配列と同じ並び順でない可能性はあっても、必ずなんらかの形でどこかに存在します。 >これも、文字列定数の領域(この場合は"ABCD")が配列の領域とは別にあるように読めてしまいます。 これもその通りでしょう。 >配列strとは別のところにある"ABC"という領域の内容を、コピーして設定する、 >ということでしょうか。 基本的にはその通りでしょう。 やろうと思えば"A B C "というところから一つおきにコピーする実装も可能でしょうけど(笑) また、(イ)と(ロ)はコンパイラ上では同じ扱いだったと思いますし、kaicheさんが書いたやり方は、実質的にコピーと同じですね。 初期化のために代入する値が結局はどこかに必要ですから。 ただ、"ABC"と連続して格納されている領域があるかないかは、実装よるでしょうから、 どちらの場合も連続領域におかないようにするコンパイラの作成は可能でしょう。 ただ、通常は効率上そういう実装はまず無いと思いますが。 ただ、この違いも使う上ではどうでもいいことですけど。 あと、気になるのは、領域というのをどういう意味で考えているかどうかですね. もし、私が考えているものより、狭い意味で使っているなら、説明も変えないといけないでしょう。 あ、私もコンパイラの実装まではやったことはありませんが、Cのコンパイル結果をアセンブラレベルで見たこととか,OSのメモリ管理とかの知識はそれなりにあります。
その他の回答 (3)
- terra5
- ベストアンサー率34% (574/1662)
>ということはこういうことですね。 はい。 あとちょっと気になったのは, >strとは違うところに"ABC"('C'の次には'\0'がありま す。)という領域が確保されていて、 の領域ですが、ここは定数領域であって変数の領域では無く、 書き換えることはできないということを把握しているかです。 確か,ANSIの規格では書き換えできないとなっていたと 思います。 ただ、古いコンパイラやOSによっては、書き換え可能な4byteの領域として扱えたケースもありました。 で、動いてしまったので間違って覚えると(^^;; それと本の記述に戻りますが, >配列の領域の確保および初期化が行われることになるので、非常にムダがあります(まず文字列定数 ですが、内容が不変の場合はそうですが、 ケースによってはそうすることが必要な場合もありますから、 一概に無駄というのは正確ではありません。 もしかすると、引用されない部分に記述があるのかも知れませんが。 あと、メジャーではないと言い切っている辺りも気になります。 その本の内容は少し注意したほうがいいかも知れません。
お礼
ご回答ありがとうございます。 char *str="ABC"; (ハ) についてですが、、 >>strとは違うところに"ABC"('C'の次には'\0'がありま >す。)という領域が確保されていて、 >の領域ですが、ここは定数領域であって変数の領域では無く、 >書き換えることはできないということを把握しているかです。 >確か,ANSIの規格では書き換えできないとなっていたと >思います。 この件については、今 私の手元にある書籍ですと、以下に書かれています。 #本の名前を挙げたところで、同じ本を持っていない人にとっては意味ないですが( ;^^)ヘ.. 『プログラミング言語C 第2版(訳書訂正版)』p.236 『新ANSI C言語辞典』(平林雅英著、技術評論社)の"文字列リテラル"の項 『C言語プログラミングの落とし穴』(柴田望洋著、ソフトバンク)p.77 あと、本の記述に関することはナルホドそうだと私も思います。
- hitomura
- ベストアンサー率48% (325/664)
(No.1の回答に対する補足を読んで) 了解、そういった背景があったのですね。 初期化するためのデータをあらかじめどこかに持っているのでは?と言う話ですね。 …と、なると、処理系に依存すると思います。 ただし、その元データに対してどうこうする事は出来ません。 writeはもちろんのこと、readもです。 …って、これはあまり自信がないなぁ…コンパイラの実装なんてやったことないから…
補足
(文字配列の初期化に関して) >ただし、その元データに対してどうこうする事は出来ません。 なるほど、つまり、たとえ初期化の元のデータが配列と別のところにあろうと さわることはできないのだから、 結局 無いのといっしょだ、ということですね。
- hitomura
- ベストアンサー率48% (325/664)
>関数の中に、 >char str[ ]="ABC"; (イ) >という宣言があるとします。(staticは付きません。) >これは、 >char str[ ]={'A', 'B', 'C', '\0'}; (ロ) >と全く同じ意味でしょうか。 はい、そのとおりです。 >似て非なるものに >char *str="ABC"; (ハ) >というものがあります。この場合は、 >strとは違うところに"ABC"('C'の次には'\0'があります。)という領域が >確保されていて、 >その先頭アドレスでstrが初期化されるのですよね。 これもそのとおりです。 >(イ)(ロ)(ハ)のいずれの場合も関数の中に書かれているとすれば、 >いずれもstrは自動変数で、関数実行時にstrの領域が確保されますよね。 (ハ)の場合はそのとおりですが、(イ)(ロ)の場合はちがいます。 確保されるはchar配列領域です。strはそのアドレスを指す定数と考えたほうがいいでしょう(この辺ちょっと自信なし)。 >(イ)は配列strの領域が確保されるときに、 >配列strとは別のところにある"ABC"という領域の内容を、コピーして設定する、 >ということでしょうか。 > >(ロ)は、配列の領域確保時にstr[0]を'A'で、str[1]を'B'で、str[2] >を'C'で、str[3]を'\0'で、初期化する、 >ということで、 >配列とは別のところには"ABC"という領域はない、 >という考えでよろしいでしょうか。 どちらも、後者の方法で初期化が行われます。したがって、どちらも配列とは別のところには"ABC"という領域はありません。 「もしそうなら…」以降は仮定が成り立たないので回答を省略します。
お礼
(この質問を締め切る前に) No.1の補足に、 >はっきり言って、これらは誤りですよね。 なる記述がありますが、基本的にはこれらの本の記述は誤りではないようです。 (ただし、柴田望洋氏の本について、注意して読んだほうがよいとのご指摘が回答No.4にあります。)
補足
(ご回答No.1を拝見した上で書きます。) ご回答有難うございます。(イ)と(ロ)について、 >どちらも、後者の方法で初期化が行われます。したがって、どちらも配列とは別のところには"ABC"という領域はありません。 明快な答えで、よくわかりました。 でも、私、いろいろ本で調べたんですが、アレレと思う記述を見つけてしまいました。 (人が書いた本まで聞かれても困る、と思われるかもしれませんが。) 「ANSI Cでも、集成体型の初期化子には、定数しか書けないという制限があります。 (中略) 集成体の初期化子に定数しか書けないことにすることで、コンパイラをちょっとばかり簡単にすることができます。あらかじめ初期値で埋めた領域をどこかに作っておいて、ブロックに突入した時点でスタックにコピーすればよいからです。」(※1) (同じページに、「実行中に、配列などの集成体型を初期化しようと思ったら」という記述があるので、集成体型には「配列」も含まれると思います。) この記述は、配列とは別のところに初期値で埋まった領域(質問でいう"ABC")がある、という意味に思えてしまいます。 また別の本には、こんな記述があります。 「K&Rでは、自動的な配列を初期化することは不可能でした。ですから、関数の中で char str[ ] = "ABCD"; (1) という定義はできませんでした。 (中略) (1)はANSIで認められた新しい方法であり、あまりメジャーではありません。実際、関数が呼び出されるたびに、配列の領域の確保および初期化が行われることになるので、非常にムダがあります(まず文字列定数"ABCD"という領域がどこかに取られます。さらに関数が呼び出されるたびに、これとは別にstrという配列の領域が確保され、その各要素が 'A', 'B', 'C', 'D', '\0' で初期化されることになります)。」(※2) これも、文字列定数の領域(この場合は"ABCD")が配列の領域とは別にあるように読めてしまいます。 はっきり言って、これらは誤りですよね。 (※1)『C言語ポインタ完全制覇』(前橋和弥著、技術評論社)p.113 (※2)『新版秘伝C言語問答ポインタ編』(柴田望洋著、ソフトバンク パブリッシング)p.63
補足
ということはこういうことですね。 (文字配列の初期化に関して) 配列とは別のところに初期化子があり、配列の領域を確保するときにそれをコピーしている。 初期化子がどこかになければ配列を初期化することは出来ない。 しかし、それは char *str="ABC"; (ハ) の"ABC"とは異なり、初期化子がどう並んでいるかはわからない。 どう並んでいてもそれを直接触ることは出来ないので関係ない。 つまり >この違いも使う上ではどうでもいいことですけど。 ということですね。 char str[ ]="ABC"; (イ) と char str[ ]={'A', 'B', 'C', '\0'}; (ロ) はまったく同じ意味だ。 他方、(ハ)は'A', 'B', 'C'がどこかにあるという点では同じでも、 ぜんぜん意味合いが異なる。