[c++] 「コンパイル時に割り当てられたメモリ」とはどういう意味ですか?



Answers

コンパイル時に割り当てられるメモリは、実行時にそれ以上割り当てが行われないことを意味します。つまり、malloc、new、またはその他の動的割り当てメソッドを呼び出すことはありません。 常にそのメモリのすべてを必要としない場合でも、一定量のメモリ使用量があります。

定義によるメモリ割り当ては実行時の概念ではないのですか?

メモリは実行時間より前に使用されていませんが実行の直前に割り付けを開始するとシステムによって処理されます。

C / C ++コードで1KBの静的に割り当てられた変数を作成すると、同じ量の実行可能ファイルのサイズが増えますか?

単に静的宣言しても、実行可能ファイルのサイズは数バイト以上増加しません。 それをゼロでない初期値で宣言すると(その初期値を保持するために) むしろリンカは、この1KBの量を、システムのローダが実行の直前に作成するメモリ要件に単に加算します。

Question

CやC ++のようなプログラミング言語では、静的で動的なメモリ割り当てをしばしば参照します。 私はコンセプトを理解していますが、「コンパイル時にすべてのメモリが割り当てられた(予約済み)」というフレーズは常に私を混乱させます。

コンパイルは、私が理解するように、高水準のC / C ++コードを機械語に変換し、実行可能ファイルを出力します。 コンパイルされたファイルのメモリはどのように「割り振られていますか? すべての仮想メモリ管理機能を備えたRAMには常にメモリが割り当てられていませんか?

定義によるメモリ割り当ては実行時の概念ではないのですか?

C / C ++コードで1KBの静的に割り当てられた変数を作成すると、同じ量の実行可能ファイルのサイズが増えますか?

これは、「静的割り当て」という見出しの下でフレーズが使用されるページの1つです。

基本に戻る:メモリ割り当て、履歴を歩く




メモリは多くの方法で割り当てられます。

  • アプリケーションヒープ内(プログラムが起動するとOS全体がヒープに割り当てられます)
  • オペレーティングシステムのヒープで(あなたはますます掴むことができます)
  • ガベージコレクタ制御ヒープ(上記と同じ)
  • スタック上に(スタックオーバフローを起こせるように)
  • バイナリ(実行可能ファイル)のコード/データセグメントに予約されています
  • 遠隔地(ファイル、ネットワーク - あなたはそのメモリへのポインタではなくハンドルを受け取る)

今あなたの質問は、 "コンパイル時に割り当てられるメモリ"です。 間違ってフレーズされた言い方ではありますが、バイナリセグメントの割り当てやスタックの割り当て、場合によってはヒープの割り当てを参照することになっていますが、その場合は目に見えないコンストラクタコールによってプログラマの目に隠れてしまいます。 または、メモリがヒープに割り当てられていないと言いたいが、スタックやセグメントの割り当てについては知らなかったと言う人がいるかもしれません(あるいはそのような詳細には行きたくありません)。

しかし、ほとんどの場合、人は、割り当てられているメモリの量がコンパイル時に分かっていると言っているだけです

バイナリサイズは、メモリがアプリケーションのコードまたはデータセグメントに予約されている場合にのみ変更されます。




スタックにNバイトを取る変数を追加しても、ビンのサイズはNバイト増加する必要はありません。 実際には、ほとんどの時間数バイトが追加されます。
まず、1000文字をコードに追加する 、ビンのサイズが線形に増加する例を見てみましょう。

1kが1000文字の文字列であれば、そのように宣言されます

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

そして、あなたはあなたのvim your_compiled_binvim your_compiled_binうとしていましたが、実際にはその文字列をどこかのビンに見ることができます。 その場合、yes:実行可能ファイルは1文字大きくなります。これは、文字列が完全に含まれているためです。
しかし、スタックにintchar 、またはlongの配列を割り当ててループ内に割り当てると、これらの行に沿ったもの

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

次に、no:それはビンを増やしません... 1000*sizeof(int)
コンパイル時の割り当てとは、あなたのコメントに基づいて理解することを意味します。コンパイルされたビンには、システムが実行されるときに必要な機能/ブロックと、アプリケーションに必要なスタックサイズ これは、システムがあなたのビンを実行するときにシステムが割り当てるもので、あなたのプログラムはプロセスになります(あなたのビンを実行することは、プロセスです...私が言っていることを得る)。
もちろん、私は完全な絵をここに描いているわけではありません。ビンには、ビンが実際に必要とするスタックの大きさに関する情報が含まれています。 この情報(とりわけ)に基づいて、システムは、スタックと呼ばれるメモリのチャンクを予約して、プログラムが自由な支配権を獲得するようにします。 スタックメモリは、プロセス(ビンの実行結果)が開始されると、システムによって割り当てられます。 その後、プロセスはスタックメモリを管理します。 関数またはループ(任意のタイプのブロック)が呼び出されたり実行されたりすると、そのブロックのローカル変数はスタックにプッシュされ、それらは削除されます(スタックメモリは話すために"解放"されます)関数/ブロック。 したがって、 int some_array[100]を宣言すると、関数Xが100*sizeof(int) +いくつかの帳簿スペースを余分に必要とすることをシステムにint some_array[100]する、数バイトの追加情報がビンに追加されます。




これらの概念については、いくつかの図を使って説明したいと思います。

コンパイル時にメモリを割り当てることができないことは確かです。 しかし、実際にコンパイル時にはどうなるのでしょうか。

ここで説明が来る。 たとえば、プログラムにはx、y、z、kの4つの変数があります。 コンパイル時には、単にメモリマップを作成します。ここでは、これらの変数の互いの位置が確認されます。 この図はそれをよりよく説明します。

想像してみましょう。プログラムはメモリ内で実行されていません。 これは大きな空の矩形で表示されます。

次に、このプログラムの最初のインスタンスが実行されます。 次のように視覚化することができます。 これは、実際にメモリが割り当てられている時間です。

このプログラムの2番目のインスタンスが実行されているとき、メモリは次のようになります。

そして3番目..

そういうわけで。

私はこの視覚化がこのコンセプトをうまく説明してくれることを望みます




あなたの疑問は、「コンパイルされたファイルにどのようにメモリが割り当てられるのですか?すべての仮想メモリ管理にRAMにメモリが常に割り当てられていませんか?定義によるメモリ割り当ては実行時の概念ではありませんか?

私は問題は、メモリ割り当てに関わる2つの異なる概念があることだと思います。 基本的に、メモリ割り当ては、「このデータ項目はこの特定のメモリに格納されている」と言うプロセスです。 最新のコンピュータシステムでは、これには2つのステップのプロセスが含まれます。

  • いくつかのシステムは、アイテムが格納される仮想アドレスを決定するために使用されます
  • 仮想アドレスは物理アドレスにマッピングされます

後者のプロセスは純粋に実行時間ですが、データが既知のサイズであり、固定数が必要な場合は、コンパイル時に実行できます。 基本的にはどのように動作するのですか:

  • コンパイラは、次のような行を含むソースファイルを表示します。

    int c;
    
  • 変数 'c​​'のメモリを予約するように指示するアセンブラの出力を生成します。 これは次のようになります。

    global _c
    section .bss
    _c: resb 4
    
  • アセンブラが実行されると、メモリ 'セグメント'(または 'セクション')の先頭から各アイテムのオフセットを追跡するカウンタが保持されます。 これは、非常に大きな '構造体'の部分に似ています。この構造体には、ファイル全体に含まれるすべてのものが含まれています。現時点では実際のメモリは割り当てられていません。 テーブルには、 _cが特定のオフセット(例えば、セグメントの先頭から510バイト)を持っていて、そのカウンタが4だけインクリメントされるので、次の変数は514バイトになります。 _cのアドレスを必要とするコードでは、出力ファイルに510を格納し、出力に_cを含むセグメントのアドレスが必要であることに注意してください。

  • リンカは、アセンブラのすべての出力ファイルを取り出し、それらを調べます。 それは、各セグメントのアドレスが重複しないように決定し、命令が依然として正しいデータ項目を参照するために必要なオフセットを追加します。 cによって占められているような初期化されていないメモリの場合(コンパイラが初期化されていないメモリ用に予約された名前である '.bss'セグメントに置かれているという事実によってメモリが初期化されないと言われた)オペレーティングシステムにどれだけ予約する必要があるかを伝えるヘッダフィールド。 それは再配置されてもよく(通常はそうですが)、通常はある特定のメモリアドレスでより効率的にロードされるように設計されており、OSはこのアドレスにロードしようとします。 この時点で、仮想アドレスがcによって使用されるものはかなり良い考えがありc

  • 物理アドレスは、プログラムが実行されるまで実際には決定されません。 しかし、プログラマの視点から見ると、物理アドレスは実際には無関係です。OSは通常、誰にも気にすることなく、頻繁に(プログラム実行中でも)変更することができます。 OSの主な目的は、とにかくこのことを抽象化することです。




私は少し前に戻る必要があると思います。 コンパイル時に割り当てられたメモリ....それはどういう意味ですか? それはまだ設計されていないコンピュータのために、まだ製造されていないチップ上のメモリが何とか予約されているということですか? いいえ、時間旅行、宇宙を操作できるコンパイラはありません。

つまり、コンパイラは実行時に何らかの形でそのメモリを割り当てるための命令を生成する必要があります。 しかし、正しい角度から見ると、コンパイラによってすべての命令が生成されるため、違いがあります。 違いは、コンパイラは実行時にコードがその決定を変更または変更できないと判断することです。 実行時にコンパイル時に50バイトが必要と判断した場合は、すでに割り当てられていると判断することはできません。




Related