1. ホーム
  2. c++

[解決済み] C++のスタック、スタティック、ヒープ

2022-04-22 20:49:39

質問

検索してみたのですが、この3つのコンセプトがよく理解できていません。ダイナミックアロケーション(ヒープ)はいつ使う必要があるのか、またその本当の利点は何なのか?スタティックとスタックの問題点は何ですか?ヒープに変数を割り当てずにアプリケーション全体を書くことは可能ですか?

他の言語では、ガベージコレクタを搭載しているため、メモリの心配をする必要がないと聞きました。ガベージコレクタは何をするのですか?

このガベージコレクターを使ってできないことで、自分でメモリーを操作できることは?

以前、この宣言で誰かが言ってました。

int * asafe=new int;

ポインタへのポインタ"があるのですが、どうすればいいですか?これはどういう意味ですか?とは違います。

asafe=new int;

?

解決方法は?

類似の質問 が出題されましたが、静力学については出題されませんでした。

スタティックメモリ、ヒープメモリ、スタックメモリとは何かについてのまとめ。

  • 静的変数は、グローバルにアクセスできないとしても、基本的にはグローバル変数です。通常、実行ファイル自体にそのアドレスがあります。プログラム全体では1つのコピーしかありません。関数呼び出し(またはクラス)に何度入っても(そして何スレッドで入っても!)、変数は同じメモリ位置を参照しています。

  • ヒープとは、動的に使用できるメモリの束のことです。もし、あるオブジェクトに 4kb が必要なら、動的アロケータはヒープ内の空き領域のリストを見て、4kb の塊を選び出し、それをあなたに渡します。一般に、動的メモリアロケータ(malloc、new など)は、メモリの終端から始まり、逆方向へ動作します。

  • スタックがどのように大きくなったり小さくなったりするかについては、この回答の範囲外ですが、常に末端からのみ追加と削除を行うということだけで十分です。スタックは通常、高い位置から始まり、低い位置へと伸びていきます。スタックが途中の動的アロケータと出会ったときに、メモリが不足します(ただし、物理メモリと仮想メモリ、フラグメンテーションを参照してください)。複数のスレッドには複数のスタックが必要です(プロセスは一般にスタックの最小サイズを予約します)。

それぞれどのような時に使いたいのか

  • スタティック/グローバルは、常に必要であることが分かっていて、決して割り当てを解除したくないメモリに便利です。(ところで、組み込み環境には静的メモリしかないと考えられるかもしれません。スタックとヒープは、第3のメモリタイプであるプログラムコードと共有される既知のアドレス空間の一部なのです。プログラムは、リンクリストのようなものが必要な場合、しばしば静的メモリから動的な割り当てを行います。しかし、静的メモリ自体(バッファ)が割り当てられるのではなく、バッファが保持するメモリから他のオブジェクトが割り当てられるのです。これは非エンベデッドでも可能で、コンソールゲームでは頻繁に内蔵のダイナミックメモリーメカニズムを排除し、すべての割り当てにプリセットサイズのバッファを使用して割り当てプロセスを厳密に制御しています)。

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

  • ヒープアロケーション(動的に割り当てられるメモリ)は、上記よりも柔軟に対応したい場合に有効です。よくあるのは、あるイベント(ユーザーが"create 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などはスクリプトには向いていますが、メインのゲームエンジンには向いていません。