それはどのようになりますか?ローカル変数のメモリは、その関数の外ではアクセスできませんか?
あなたはホテルの部屋を借ります。ベッドサイドテーブルの上の引き出しに本を入れて、寝ます。あなたは翌朝チェックアウトしますが、鍵を返却することを「忘れて」ください。あなたは鍵を盗みます!
1週間後、ホテルに戻り、チェックインせずに、盗まれた鍵を使って古い部屋に忍び込み、引き出しを調べます。あなたの本はまだそこにあります。びっくり!
それはどうしてですか?部屋を借りていなければ、ホテルの部屋の引き出しの中身にアクセスできませんか?
まあ、明らかにそのシナリオは現実の世界で問題なく起こります。部屋にいる権限がなくなったときに、本が消えるような不可解な力はありません。盗まれた鍵で部屋に入るのを妨げる不思議な力もありません。
ホテルの管理者があなたの本を削除する必要はありません。あなたは彼らと契約を結ばなかった、あなたが物を残しておくと彼らはあなたのためにそれを細断するだろうと言った。盗まれたキーを使って違法に部屋に戻ってそれを取り戻す場合、ホテルのセキュリティスタッフはあなたがこっそり侵入するのを捕まえる必要はありません。あなたが彼らと契約していませんでした。部屋は後で、あなたは私を止める必要があります。」むしろ、あなたは彼らと契約し、あなたが破った契約「私は後で私の部屋にこっそり戻ってこないことを約束する」と言った。
この状況では、何かが起こる可能性があります。本はそこにあることができます-あなたは幸運になりました。他の誰かの本がそこにあり、あなたの本がホテルのかまどにあるかもしれません。あなたが本をばらばらに引き裂いて入ってくると、誰かがそこにいる可能性があります。ホテルはテーブルを削除して完全に予約し、ワードローブに置き換えることができました。ホテル全体が取り壊されてフットボールスタジアムに置き換えられようとしている可能性があり、こっそりと潜っている間に爆発で死ぬことになります。
何が起こるか分からない。ホテルをチェックアウトし、後で違法に使用するための鍵を盗んだとき、システムの規則を破ることを選択したため、予測可能な安全な世界に住む権利を放棄しました。
C ++は安全な言語ではありません。それは陽気にあなたがシステムのルールを破ることを可能にします。許可されていない部屋に戻って、もうそこにはないかもしれない机をくまなく探すなど、違法で愚かなことをしようとした場合、C ++はあなたを止めません。C ++よりも安全な言語は、たとえばキーをはるかに厳密に制御することによって、能力を制限することでこの問題を解決します。
更新
聖なる良さ、この答えは多くの注目を集めています。(理由はわかりません-私はそれを単なる「楽しい」小さなアナロジーと考えましたが、何でも。)
もう少し技術的な考えでこれを少し更新することは密接な関係があるのではないかと思いました。
コンパイラーは、そのプログラムによって操作されるデータのストレージを管理するコードを生成するビジネスです。メモリを管理するためのコードを生成するにはさまざまな方法がありますが、時間の経過とともに2つの基本的な手法が定着しました。
1つ目は、ストレージ内の各バイトの「存続時間」、つまり、あるプログラム変数と有効に関連付けられている期間を予測できない、ある種の「長命」のストレージ領域を用意することです。時間の。コンパイラーは、「ヒープ・マネージャー」への呼び出しを生成します。このマネージャーは、ストレージが必要なときに動的に割り振り、不要になったときにそれを再利用する方法を知っています。
2番目の方法は、各バイトの存続期間がよくわかっている「短命」のストレージ領域を用意することです。ここでは、寿命は「ネスト」パターンに従います。これらの短命変数の中で最も長命の変数は、他の短命変数の前に割り当てられ、最後に解放されます。存続期間の短い変数は、存続期間の最も長い変数の後に割り当てられ、それらの前に解放されます。これらの寿命の短い変数の寿命は、寿命の長い変数の寿命内に「ネスト」されます。
ローカル変数は後者のパターンに従います。メソッドに入ると、そのローカル変数が有効になります。そのメソッドが別のメソッドを呼び出すと、新しいメソッドのローカル変数が有効になります。最初のメソッドのローカル変数が死ぬ前に死んでしまいます。ローカル変数に関連付けられたストレージの存続期間の開始と終了の相対的な順序は、事前に計算できます。
このため、スタックには最初にプッシュされたものが最後に取り出されるというプロパティがあるため、ローカル変数は通常「スタック」データ構造のストレージとして生成されます。
それは、ホテルが順番に部屋を貸し出すことだけを決定するようなもので、あなたがチェックアウトした部屋番号よりも大きい部屋番号を持つ全員がチェックアウトするまではチェックアウトできません。
スタックについて考えてみましょう。多くのオペレーティングシステムでは、スレッドごとに1つのスタックが取得され、スタックは特定の固定サイズになるように割り当てられます。メソッドを呼び出すと、スタックにデータがプッシュされます。次に、元の投稿者がここで行うように、スタックへのポインターをメソッドから戻す場合、それは完全に有効な100万バイトのメモリブロックの中央へのポインターにすぎません。私たちの類推では、ホテルをチェックアウトします。あなたがそうするとき、あなたはちょうど最も高い番号の占有された部屋からチェックアウトしました。他の誰もあなたの後にチェックインせず、あなたが違法にあなたの部屋に戻った場合、あなたのすべてのものはこの特定のホテルにまだそこにあることが保証されています。
スタックは非常に安くて簡単なので、一時ストアにスタックを使用します。C ++の実装は、ローカルの格納にスタックを使用する必要はありません。ヒープを使用できます。そうしないと、プログラムが遅くなります。
C ++の実装では、スタックに残されたガベージをそのまま残して、後で不正に戻ってくることがないようにする必要はありません。コンパイラーが、空いたばかりの「部屋」のすべてをゼロに戻すコードを生成することは完全に合法です。繰り返しますが、それは高価になるからです。
C ++の実装は、スタックが論理的に縮小したときに、以前は有効であったアドレスが引き続きメモリにマッピングされることを保証するために必要ではありません。実装は、オペレーティングシステムに「このスタックのページを使用して完了しました。別の言い方をするまで、以前に有効なスタックページに誰かが触れた場合にプロセスを破棄する例外を発行する」ことを通知できます。繰り返しになりますが、実装は遅くて不必要なので、実際にはそうしません。
代わりに、実装ではミスを犯してそれを回避できます。ほとんどの時間。ある日まで、本当にひどいことが起こり、プロセスが爆発します。
これは問題があります。多くのルールがあり、それらを誤って破るのは非常に簡単です。確かに何度も持っています。さらに悪いことに、破損が発生してから何十億ナノ秒もメモリが破損していることが検出された場合にのみ問題が表面化します。
より多くのメモリセーフ言語はあなたの力を制限することによってこの問題を解決します。「通常の」C#では、ローカルのアドレスを取得して返すか、後で使用するために保存する方法はありません。ローカルのアドレスを取得できますが、言語は巧妙に設計されているため、ローカルの存続期間が終了すると使用できなくなります。ローカルのアドレスを取得してそれを返すには、コンパイラを特別な「安全でない」モードにし、プログラムに「安全でない」という単語を入れて、おそらく実行しているという事実に注意を向ける必要があります。ルールを破る危険な何か。
さらに読むために:
address of local variable ‘a’ returned
ます。valgrindショーInvalid write of size 4 [...] Address 0xbefd7114 is just below the stack ptr