C++ のスタック、静的、ヒープ 質問する

C++ のスタック、静的、ヒープ 質問する

調べてみましたが、この 3 つの概念がよくわかりません。動的割り当て (ヒープ内) はいつ使用する必要がありますか。また、その実際の利点は何ですか。静的割り当てとスタックの問題は何ですか。ヒープ内に変数を割り当てずにアプリケーション全体を作成できますか。

他の言語では「ガベージ コレクター」が組み込まれているため、メモリを気にする必要がないと聞きました。ガベージ コレクターは何をしますか?

このガベージ コレクターを使用してできなかったことを、自分でメモリを操作することで何ができるでしょうか。

かつて誰かが私にこう言いました。

int * asafe=new int;

「ポインタへのポインタ」があります。これはどういう意味ですか? これは次のものとは異なります:

asafe=new int;

?

ベストアンサー1

同様の質問質問されましたが、静力学については質問されませんでした。

静的メモリ、ヒープ メモリ、スタック メモリの概要:

  • 静的変数は、グローバルにアクセスできない場合でも、基本的にはグローバル変数です。通常、そのアドレスは実行可能ファイル自体にあります。プログラム全体でコピーは 1 つだけです。関数呼び出し (またはクラス) を何回実行しても (また、スレッドの数に関係なく)、変数は同じメモリ位置を参照します。

  • ヒープは、動的に使用できるメモリの集まりです。オブジェクトに 4kb が必要な場合、動的アロケータはヒープ内の空き領域のリストを調べ、4kb のチャンクを選択して割り当てます。通常、動的メモリ アロケータ (malloc、new など) は、メモリの最後から開始して逆方向に処理します。

  • スタックがどのように拡大縮小するかを説明するのは、この回答の範囲外ですが、常に最後からのみ追加および削除すると言っておけば十分でしょう。スタックは通常、高いアドレスから始まり、低いアドレスに向かって拡大します。スタックが中間のどこかで動的アロケータに遭遇すると、メモリが不足します (ただし、物理メモリと仮想メモリおよび断片化を参照してください)。複数のスレッドには複数のスタックが必要です (プロセスは通常、スタックの最小サイズを予約します)。

それぞれをいつ使用するか:

  • 静的/グローバルは、常に必要で、割り当てを解除したくないことが分かっているメモリに便利です。(ちなみに、組み込み環境は静的メモリのみを持つと考えられます。スタックとヒープは、3 番目のメモリ タイプ (プログラム コード) によって共有される既知のアドレス空間の一部です。プログラムは、リンク リストなどが必要な場合、静的メモリから動的割り当てを行うことがよくあります。ただし、静的メモリ自体 (バッファー) は「割り当てられる」のではなく、この目的のためにバッファーによって保持されるメモリから他のオブジェクトが割り当てられます。これは組み込み以外の場合でも実行でき、コンソール ゲームでは、組み込みの動的メモリ メカニズムを避けて、すべての割り当てに事前設定されたサイズのバッファーを使用して割り当てプロセスを厳密に制御することがよくあります。)

  • スタック変数は、関数がスコープ内 (スタックのどこか) にある限り、変数を残しておきたい場合に便利です。スタックは、変数が配置されているコードに必要な変数であっても、そのコード外では必要ない場合に便利です。また、ファイルなどのリソースにアクセスしていて、そのコードを離れるときにリソースを自動的に削除したい場合にも非常に便利です。

  • ヒープ割り当て (動的に割り当てられたメモリ) は、上記よりも柔軟性を高めたい場合に便利です。関数は、イベント (ユーザーが [ボックスの作成] ボタンをクリックする) に応答するために頻繁に呼び出されます。適切な応答には、関数が終了した後も長く残る新しいオブジェクト (新しい Box オブジェクト) を割り当てる必要がある場合があります。そのため、このオブジェクトはスタック上に存在できません。ただし、プログラムの開始時に必要なボックスの数はわからないため、静的にすることはできません。

ガベージコレクション

最近、ガベージ コレクターの素晴らしさについてよく耳にするので、少し異論を唱えた方が役に立つかもしれません。

ガベージ コレクションは、パフォーマンスがそれほど問題にならない場合には素晴らしいメカニズムです。GC はどんどん改良され、洗練されてきていると聞きますが、実際にはパフォーマンスの低下を余儀なくされる可能性があります (使用例によって異なります)。また、怠惰な場合は、それでも適切に動作しない可能性があります。最良の場合、ガベージ コレクターは、メモリへの参照がなくなったことを認識すると、メモリが消去されることを認識します (参照:参照カウント)。しかし、自分自身を参照するオブジェクトがある場合 (おそらく、別のオブジェクトを参照し、そのオブジェクトが参照し返すことによって)、参照カウントだけではメモリを削除できるかどうかはわかりません。この場合、GC は参照スープ全体を調べて、自分自身だけが参照するアイランドがあるかどうかを調べる必要があります。すぐに O(n^2) 操作になると思いますが、それが何であれ、パフォーマンスを気にするなら、これは悪くなる可能性があります。(編集: Martin B指摘している適度に効率的なアルゴリズムの場合は O(n) です。パフォーマンスを重視し、ガベージ コレクションなしで一定時間で割り当てを解除できる場合は、それでも O(n) は大きすぎます。

個人的には、C++ にはガベージ コレクションがないという話を聞くと、それを C++ の機能だと思い込んでしまいますが、おそらく私は少数派でしょう。C と C++ のプログラミングを学ぶ上で最も難しいのは、ポインターと、その動的メモリ割り当てを正しく処理する方法です。Python などの他の言語は、GC がないとひどいものになるので、言語に何を求めるかが問題だと思います。信頼できるパフォーマンスを求めるなら、ガベージ コレクションのない C++ が、Fortran 以外で私が思いつく唯一のものです。使いやすさと補助輪 (「適切な」メモリ管理を学ばなくてもクラッシュを防ぐ) を求めるなら、GC 付きのものを選んでください。メモリの管理方法をよく知っていても、他のコードの最適化に費やす時間を節約できます。パフォーマンスの低下はそれほどありませんが、信頼できるパフォーマンス (および、裏で何がいつ起こっているかを正確に把握する機能) が本当に必要なら、C++ を使い続けるでしょう。私が今までに聞いた主要なゲーム エンジンがすべて C++ (C またはアセンブリではない) であるのには理由があります。Python などはスクリプトには適していますが、メインのゲーム エンジンには適していません。

おすすめ記事