- ベストアンサー
消滅したローカル変数のメモリ領域への上書きについて
- ローカル変数が消滅した際にメモリ領域が上書きされる仕組みについて質問しています。
- ソースコードからはローカル変数が上書きされることがわかりませんでした。
- Val1=-200,Val2=-100という結果が出た理由について質問しています。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
思いっきり処理系依存な話ですが、一般的なC言語処理系というかコンパイラの出力では、そのような答えになる場合が多いと思います。 なぜそうなるのかはC言語のソースではなく、アセンブラ出力を見れば確実にわかりますが、 ・C言語のたいていの処理系(コンパイラ)では、自動変数は「スタック」を使って実現しています。 ・大抵の実行環境(CPU)では、スタックはアドレスの「後ろ」から順に使っていくようになっています。 以下、int が4バイトとして書きます。 ・first関数 スタックの末尾に8バイトを自動変数 comp 用に確保します。 その8バイト内では、構造体のメンバが val1 、val2 の順に並んでますから、 val1 のアドレス: スタック末尾-8バイト(変数の確保位置)+0バイト(構造体内での位置) val2 のアドレス: スタック末尾-8バイト(変数の確保位置)+4バイト(構造体内での位置)= スタック末尾-4バイト になります。 ・second 関数 変数a、変数bの順にスタック末尾から4バイトずつ確保されます。 a のアドレス: スタック末尾-4バイト b のアドレス: aのアドレス-4バイト = スタック末尾-8バイト 結果、comp.val1 とb、comp.val2 とa がスタック上の同じアドレスに割り当てられることになります。 main 関数内で、 printf("&argc=%p, &argv=%p, &ret=%p\n", &argc, &argv, &ret); first 関数内で、printf("&comp=%p, &comp.val1=%p, &comp.val2=%p\n", &comp, &(comp.val1), &(comp.val2)); second 関数内で、 printf("&a=%p, &b=%p\n", &a, &b); というコードを追加してみれば、どんな感じでスタックが消費されてアドレスが変わっていくのかが少しは見えると思います。
その他の回答 (3)
- aigaion
- ベストアンサー率47% (287/608)
>ソースを見ただけでわかるものなのでしょうか わかりません. ただ,処理系依存なので,処理系を併記すればわかる人はわかります. 関数を呼ぶときは,スタックに制御情報が積まれて,引数が積まれて,ローカル変数が積まれる. (スタックだと思ってください. | firstのローカル変数 | firstの引数 | firstの制御情報 | mainの情報 first関数が終わってmainに戻ったら | | | | mainの情報 となるが,firstの情報は削除されることなく残っている. ローカル変数のポインタを取得しておけば 後に積まれることになるであろうsecondのローカル変数の位置がわかる. で,secondを呼び出すと | secondのローカル変数 | secondの引数 | secondの制御情報 | mainの情報 ここで,secondから戻ってきた直後は,secondのローカル変数の値は保持されている. firstを読んだ時に,ローカル変数へのポインタわかっているので,プログラムのように出力が可能. 処理系依存なのですべてのコンパイラで可能なわけではありません. このあたりのことをふまえた上での課題ですかね? 違うなら,前の回答者さんがおっしゃってる通り ローカル変数へのポインタ参照は危ないのでやめましょう ってことで終わりです. 上のようなことを考えてなら,次のような理由だと思います. aとbでは,スタックにa,bの順番で積み | b = -200 | a = -100 | 制御情報 | mainの情報 と積まれるが comp_val は構造体なので | comp_val.val1 | comp_val.val2 | 制御情報 | mainの情報 comp_valという1つの塊としてスタックに積まれてしまったため順番が逆になったものと思います.
お礼
大変遅くなってしまいましてごめんなさい、解答ありがとうございます。 構造体と普通の値ではスタックの積み方が異なるんですね...詳しい説明ありがとうございました。
バグはNo.1さんのおっしゃる通りとして、 多分、わざと危険なことをしてローカル変数を潰す 実験だと思います。 それで、No.1さんのおっしゃる通り、 ”いつ解放されるかわからないローカル変数のポインタを返してはいけません。” がプログラマの鉄則で、こういうプログラムは通常存在しません。 あくまで、ローカル変数のメモリ領域への上書きの実験的意味合いが強いのでしょう。 順序が逆になる点は、多分、Cのコンパイルプログラムの作りの問題で、structのときだけ、たまたま、メモリの割りあて順序を逆にしているというだけで、そのような規則はないと思いますよ。 したがって、”それはソースを見ただけでわかるものなのでしょうか。”についてはわからないと思います。 ちなみに言えば、Cのコンパイルプログラムの作りによって、 ローカル変数は、上記プログラムで、必ず潰れるとも限らないと思います。
お礼
大変遅くなってしまいましてごめんなさい、解答ありがとうございます。 有名なプログラミング言語でも、規則通りに作らないとランダム性が出てしまうんですね。
- D-Matsu
- ベストアンサー率45% (1080/2394)
> ret=first(val); 変数valの宣言がどこにもありませんね、というかfirstは引数を取らないんでは。 > comp_t first(){ > comp_val comp; > comp.val1=10; > comp.val2=20; > return ∁ > } いつ解放されるかわからないローカル変数のポインタを返してはいけません。メモリ領域を破壊しかねない危険行為です。 > void second(){ > int a=-100; > int b =-200; > } これではsecond()も新たにローカル変数を作ってそこに値を入れてるだけなので、ret(first()内のcomp)のメンバーには影響を与えません。 本当にこのソースで完全(first()に絡む間違いはさておき)なのであれば、「たまたまval1,val2(がかつて存在したアドレス)にその値が入っていた」だけにしか見えません。
補足
すみません。first(val)はfirst()の間違いでした。 このソースで、printfがどのように表示されるか、というのが課題です。 ローカル変数のポインタを返しているのはわざとだと思われます。
お礼
大変遅くなってしまいましてごめんなさい、解答ありがとうございます。 追加コードありがとうございます。さっそく試してみます。