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