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


159

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

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

定義によるメモリ割り当ては、ランタイムコンセプトではありませんか?

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

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

基本に戻る:メモリー割り当て、歴史の歩み


ほとんどの最新のアーキテクチャでは、コードとデータが完全に分離されています。ソースファイルには同じ場所に両方のコードデータが含まれていますが、ビンにはデータへの参照しかありません。つまり、ソース内の静的データは参照としてのみ解決されます。
Cholthi Paul Ttiopic 16年

回答:


184

コンパイル時に割り当てられるメモリとは、コンパイラがコンパイル時に解決し、プロセスメモリマップ内に特定のものが割り当てられることを意味します。

たとえば、グローバル配列について考えてみます。

int array[100];

コンパイラーは、コンパイル時に配列のサイズとのサイズをint知っているので、コンパイル時に配列全体のサイズを知っています。また、グローバル変数にはデフォルトで静的ストレージ期間があります。これは、プロセスメモリ空間の静的メモリ領域(.data / .bssセクション)に割り当てられます。その情報が与えられると、コンパイラーは、コンパイル中に、その静的メモリー領域のどの配列であるかを決定します

もちろん、そのメモリアドレスは仮想アドレスです。プログラムは、独自のメモリスペース全体(0x00000000から0xFFFFFFFFなど)を持っていると想定しています。これが、コンパイラが「OK、配列はアドレス0x00A33211にある」などの仮定を行うことができる理由です。実行時に、そのアドレスはMMUおよびOSによって実際の/ハードウェアアドレスに変換されます。

値が初期化された静的ストレージのものは少し異なります。例えば:

int array[] = { 1 , 2 , 3 , 4 };

最初の例では、コンパイラは配列が割り当てられる場所のみを決定し、その情報を実行可能ファイルに格納しました。
値で初期化されたものの場合、コンパイラーは配列の初期値も実行可能ファイルに挿入し、プログラムの開始時に配列を割り当てた後、配列にこれらの値を入力する必要があることをプログラムローダーに通知するコードを追加します。

コンパイラによって生成されたアセンブリの2つの例を次に示します(x86ターゲットを使用したGCC4.8.1)。

C ++コード:

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

出力アセンブリ:

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

ご覧のとおり、値は直接アセンブリに注入されます。配列aでは、コンパイラは16バイトのゼロ初期化を生成します。これは、標準では静的に格納されたものをデフォルトでゼロに初期化する必要があるためです。

8.5.9(イニシャライザ)[注意]:
静的ストレージ期間のすべてのオブジェクトは、他の初期化が行われる前に、プログラムの起動時にゼロで初期化されます。場合によっては、後で追加の初期化が行われます。

コンパイラーがC ++コードで実際に何をするかを確認するために、常にコードを逆アセンブルすることをお勧めします。これは、ストレージクラス/期間(この質問のような)から高度なコンパイラ最適化に適用されます。コンパイラにアセンブリを生成するように指示することもできますが、これをインターネット上で友好的な方法で行うための素晴らしいツールがあります。私のお気に入りはGCC Explorerです。


2
ありがとう。これにより、多くのことが明らかになります。そのため、コンパイラーは「0xABCから0xXYZまでのメモリを変数array []などに予約する」と同等のものを出力します。そしてローダーはそれを使用して、プログラムを実行する直前に実際に割り当てますか?
Talhaは2014年

1
@TalhaSayed正確に。編集を参照して例を確認してください
Manu343726 '25

2
@Secko簡略化しました。プログラムについてのその唯一の言及は仮想メモリを介して機能しますが、質問は仮想メモリについてではないので、トピックを拡張していません。仮想メモリのおかげで、コンパイラはコンパイル時にメモリアドレスに関する仮定を行うことができることだけを指摘しました。
Manu343726 2014年

2
@Seckoはい。「生成された」mmmの方がいいと思います。
Manu343726 2014年

2
「それはプロセスのメモリ空間の静的なメモリ領域に割り当てられています私のプロセスのメモリ空間にいくつかの静的な乳房領域を割り当てた読書。
Radiodef、2014年

27

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

定義によるメモリ割り当ては、ランタイムコンセプトではありませんか?

メモリーは実行時より前は使用されていませんが実行開始の直前に、その割り当てはシステムによって処理されます。

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

staticを宣言するだけでは、実行可能ファイルのサイズは数バイトを超えません。非ゼロの初期値で宣言すると(その初期値を保持するため)。むしろ、リンカはこの1KBの量を、システムのローダーが実行の直前に作成するメモリ要件に追加するだけです。


1
書き込むstatic int i[4] = {2 , 3 , 5 ,5 }と、実行可能ファイルのサイズが16バイト増加します。「単純に静的を宣言しても、実行可能ファイルのサイズが数バイトを超えて大きくなることはありません。ゼロ以外の初期値を使用して宣言すると、初期値を使用して宣言すると、意味がわかります。
Suraj Jain

実行可能ファイルには、静的データ用の2つの領域があります。1つは初期化されていない静的領域用で、もう1つは初期化された静的領域用です。初期化されていない領域は、実際には単なるサイズの指標です。プログラムが実行されると、そのサイズは静的ストレージ領域を拡張するために使用されますが、プログラム自体は、初期化されていないデータが使用される量以上のものを保持する必要はありませんでした。初期化されたstaticの場合、プログラムは(それぞれの)staticのサイズだけでなく、初期化先のサイズも保持する必要があります。したがって、例では、プログラムには2、3、5、および5が含まれます。
2016

配置場所/割り当て方法に関して定義された実装ですが、知っておく必要があることを理解できていません。
MAH

23

コンパイル時に割り当てられるメモリとは、プログラムをロードすると、メモリの一部がすぐに割り当てられ、この割り当てのサイズと(相対)位置がコンパイル時に決定されることを意味します。

char a[32];
char b;
char c;

これらの3つの変数は「コンパイル時に割り当て」られます。これは、コンパイラがコンパイル時にそれらのサイズ(固定)を計算することを意味します。変数aはメモリ内のオフセットになります。たとえば、アドレス0を指すと、bアドレス33およびc34を指します(アライメントの最適化がない場合)。したがって、1Kbの静的データを割り当てても、コード内のオフセットが変更されるだけなので、コードのサイズは増えません実際のスペースはロード時に割り当てられます

カーネルは常に追跡し、内部データ構造を更新する必要があるため、実際のメモリ割り当ては常に実行時に行われます(各プロセス、ページなどに割り当てられるメモリの量)。違いは、コンパイラーは使用する各データのサイズをすでに認識しており、これはプログラムが実行されるとすぐに割り当てられることです。

また、相対アドレスについて話していることも忘れないでください。変数が配置される実際のアドレスは異なります。ロード時に、カーネルはプロセス用にいくらかのメモリを予約します。たとえば、at addressと言います。x実行可能ファイルに含まれるすべてのハードコーディングされたアドレスはxバイト単位でインクリメントされるためa、この例の変数はaddress x、b at address x+33およびなど。


17

スタックにNバイトを占める変数を追加しても、(必然的に)ビンのサイズはNバイト増加しません。実際には、ほとんどの場合数バイトが追加されます。
コードに1000文字を追加することで、ビンのサイズが線形的に増加する例から始めましょう。

1kが1000文字の文字列の場合、そのように宣言されます

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

するとvim your_compiled_bin、実際にビンのどこかでその文字列を見ることができます。その場合、はい:実行可能ファイルには文字列全体が含まれているため、1 k大きくなります。
ただし、ints、chars、またはlongs の配列をスタックに割り当ててループに割り当てる場合、これらの行に沿って何か

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

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


どうもありがとう。もう1つの質問、関数のローカル変数もコンパイル時に同じ方法で割り当てられますか?
Talhaは2014年

@TalhaSayed:はい、それは私が言ったときに私が意味したものです:「システムが必要とする情報は、どの機能/ブロックが必要とするメモリの量を知る必要があるのか​​」関数を呼び出すと、システムはその関数に必要なメモリを割り当てます。関数が戻ると、そのメモリは再び解放されます。
エリアスファンOotegem 2014年

あなたのCコードのコメントに関して:それは実際に/必要に何が起こるかではありません。たとえば、文字列はコンパイル時に1回だけ割り当てられる可能性が高いです。したがって、「解放」されることはなく(また、用語は通常、動的に何かを割り当てる場合にのみ使用されると思います)、i「解放」されません。iメモリに常駐する場合、それはスタックにプッシュされるだけであり、その意味では解放されないものであり、それを無視するic、レジスタ全体に保持されます。もちろん、これはすべてコンパイラに依存します。つまり、それはそれほど白黒ではありません。
phant0m 2014年

@ phant0m:文字列がスタックに割り当てられると決して言っていません。ポインタもそうであるだけで、文字列自体は読み取り専用メモリに常駐します。ローカル変数に関連付けられているメモリがfree()呼び出しの意味で解放されないことはわかっていますが、それらが使用したスタックメモリは、リストした関数が戻ると、他の関数で自由に使用できます。混乱を招く可能性があるため、コードを削除しました
エリアスヴァンウーテゲム2014年

ああ、なるほど。その場合、私のコメントを「私はあなたの言葉遣いに混乱しました」という意味だと思います。
phant0m 2014年

16

多くのプラットフォームでは、各モジュール内のすべてのグローバルまたは静的割り当ては、コンパイラーによって3つ以下の統合割り当て(1つは初期化されていないデータ(多くの場合「bss」と呼ばれる)、もう1つは初期化された書き込み可能データ(多くの場合「データ」と呼ばれる)に統合されます。 )、および定数データ( "const"))の1つと、プログラム内の各タイプのグローバルまたは静的割り当てのすべてが、リンカーによって各タイプの1つのグローバルに統合されます。たとえば、intが4バイトであると仮定すると、モジュールの唯一の静的割り当ては次のとおりです。

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

bssには208バイト、「data」には16バイト、「const」には28バイトが必要であることをリンカに伝えます。さらに、変数への参照はエリアセレクターとオフセットに置き換えられるため、a、b、c、d、eはbss + 0、const + 0、bss + 4、const + 24、dataに置き換えられます。それぞれ+0、またはbss + 204。

プログラムがリンクされると、すべてのモジュールのすべてのbss領域が連結されます。同様に、データと定数領域。各モジュールについて、すべてのbss相対変数のアドレスは、先行するすべてのモジュールのbss領域のサイズによって増加します(これも同様に、dataおよびconstを使用)。したがって、リンカが完了すると、どのプログラムにも1つのbss割り当て、1つのデータ割り当て、および1つのconst割り当てがあります。

プログラムがロードされると、一般的に、プラットフォームに応じて次の4つのいずれかが発生します。

  1. 実行可能ファイルは、データの種類ごとに必要なバイト数と、初期コンテンツが見つかった初期化されたデータ領域を示します。また、bss-、data-、またはconst-相対アドレスを使用するすべての命令のリストも含まれます。オペレーティングシステムまたはローダーは、各領域に適切な量の領域を割り当て、その領域の開始アドレスを、それを必要とする各命令に追加します。

  2. オペレーティングシステムは、3種類のデータすべてを保持するためにメモリのチャンクを割り当て、アプリケーションにそのメモリのチャンクへのポインタを提供します。静的データまたはグローバルデータを使用するコードは、そのポインターに関連してそれを逆参照します(多くの場合、ポインターはアプリケーションの存続期間中、レジスターに格納されます)。

  3. オペレーティングシステムは、バイナリコードを保持するものを除いて、最初はアプリケーションにメモリを割り当てませんが、アプリケーションが最初に行うことは、オペレーティングシステムからの適切な割り当てを要求することです。

  4. オペレーティングシステムは最初はアプリケーションに領域を割り当てませんが、アプリケーションは起動時に適切な割り当てを要求します(上記のとおり)。アプリケーションには、メモリが割り当てられた場所を反映するために更新する必要のあるアドレスの付いた命令のリストが含まれます(最初のスタイルと同様)。ただし、OSローダーによってアプリケーションにパッチを適用するのではなく、アプリケーション自体にパッチを適用するのに十分なコードが含まれます。 。

4つのアプローチにはすべて、長所と短所があります。ただし、いずれの場合も、コンパイラーは任意の数の静的変数を固定された少数のメモリー要求に統合し、リンカーはそれらすべてを少数の統合された割り当てに統合します。アプリケーションはオペレーティングシステムまたはローダーからメモリのチャンクを受信する必要がありますが、それを必要とするすべての個々の変数にその大きなチャンクから個々の部分を割り当てるのは、コンパイラーとリンカーです。


13

あなたの質問の核心はこれです:「コンパイルされたファイルでメモリはどのように「割り当て」られますか?メモリは常にすべての仮想メモリ管理機能を備えたRAMに割り当てられているのではありませんか?定義によりメモリ割り当てはランタイムの概念ではありませんか?」

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

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

後者のプロセスは純粋に実行時ですが、データが既知のサイズであり、それらの固定数が必要な場合は、前者をコンパイル時に実行できます。基本的には次のようになります。

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

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

    global _c
    section .bss
    _c: resb 4
  • アセンブラを実行すると、メモリの「セグメント」(または「セクション」)の先頭からの各アイテムのオフセットを追跡するカウンターが保持されます。これは、ファイル全体のすべてを含む非常に大きな「構造体」の一部のようなもので、現時点では実際のメモリは割り当てられておらず、どこにあってもかまいません。これ_cは、特定のオフセット(たとえば、セグメントの先頭から510バイト)があるテーブルに記録し、そのカウンターを4増分するため、次の変数は(たとえば)514バイトになります。のアドレスを必要とするコードの場合_c、出力ファイルに510を配置し、_c後で追加することを含むセグメントのアドレスが出力に必要であるというメモを追加します。

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

  • 物理アドレスは、プログラムが実行されるまで実際には決定されません。ただし、プログラマーの観点から見ると、物理アドレスは実際には無関係です。それが何であるかを見つけることさえできません。OSは通常、誰かに話しかけることはないため、頻繁に(プログラムの実行中でも)変更される可能性があるためです。 OSの主な目的はとにかくこれを抽象化することです。


9

実行可能ファイルは、静的変数に割り当てるスペースを記述します。この割り当ては、実行可能ファイルを実行するときにシステムによって行われます。したがって、1kBの静的変数は1kBの実行可能ファイルのサイズを増やしません。

static char[1024];

もちろんイニシャライザを指定しない限り:

static char[1024] = { 1, 2, 3, 4, ... };

したがって、「機械語」(つまり、CPU命令)に加えて、実行可能ファイルには必要なメモリレイアウトの説明が含まれています。


5

メモリはさまざまな方法で割り当てることができます。

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

ここでの質問は、「コンパイル時に割り当てられるメモリ」とは何かということです。間違いなく、これは誤った言い回しであり、バイナリセグメントの割り当てまたはスタックの割り当て、あるいは場合によってはヒープの割り当てを指すことになっていますが、その場合、割り当ては目に見えないコンストラクタ呼び出しによってプログラマの目から隠されています。または、おそらくメモリはヒープに割り当てられていないと言いたいが、スタックまたはセグメントの割り当てについては知らなかったと言った人(またはその種の詳細に進みたくなかった人)。

しかし、ほとんどの場合、割り当てられるメモリの量はコンパイル時に既知であると言いたいだけです。

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


1
この回答は、「アプリケーションヒープ」、「OSヒープ」、および「GCヒープ」について、これらがすべて意味のある概念であるかのように説明しているため、混乱しています(または混乱しています)。私は#1までに、一部のプログラミング言語が.dataセクションの固定サイズのバッファーからメモリを割り当てる「ヒープ割り当て」スキームを(想定上)使用する可能性があると言っていたと推測しますが、それは有害であるほど現実的ではないようですOPの理解に。#2と#3に関して、GCの存在は実際には何も変更しません。そして、#5再、あなたが間に比較的はるかに重要な区別を省略.dataして.bss
Quuxplusone 2014年

4

あなたが正しいです。メモリは実際にロード時に割り当てられます(ページングされます)。つまり、実行可能ファイルが(仮想)メモリに読み込まれたときです。その瞬間にメモリを初期化することもできます。コンパイラはメモリマップを作成するだけです。[ちなみに、スタックとヒープのスペースもロード時に割り当てられます!]


2

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

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


私はSocraticメソッドを使用する回答が好きですが、「コンパイラーは実行時になんらかの方法でそのメモリーを割り当てるための命令を生成する」という誤った結論にあなたを反対しました。コンパイラが実行時の「命令」を生成せずに「メモリを割り当てる」方法を確認するには、上位投票の回答を確認してください。(アセンブリ言語のコンテキストでの「命令」には特定の意味、つまり実行可能なオペコードがあることに注意してください。口語で「レシピ」のような意味で単語を使用しているかもしれませんが、このコンテキストではOPを混乱させるだけです。 )
Quuxplusone 2014年

1
@Quuxplusone:私はその答えを読んだ(そして賛成した)。いいえ、私の答えは初期化された変数の問題を具体的に扱っていません。また、自己変更コードには対応していません。その答えはすばらしいものですが、私が重要な問題と考えるもの、つまり状況を把握することについては触れていません。したがって、私の答えは、OP(および他の人)が理解できない問題を抱えているときに、OP(および他の人)が停止し、何が起こっているのか、または何が起こり得るのかを考えるのに役立つと思います。
jmoreno 2014年

@Quuxplusone:私がここで誤った主張をしている場合は申し訳ありませんが、あなたも私の答えを-1にした人物の1人だったと思います。もしそうなら、私の答えのどの部分がそうするための主な理由であったかをひどく指摘してもよろしいですか、そして私の編集をチェックしてもいいですか?スタックメモリの管理方法の真の内部については少しスキップしているので、答えを100%正確ではないことについて少し追加しました:)
Elias Van Ootegem

@jmoreno「設計されていないコンピューターのために、まだ製造されていないチップ上のメモリが何らかの理由で予約されていることを意味することはできますか?」「割り当て」という言葉が意味するのは、最初から混乱した誤解です。私が指摘しようとしていた問題を正確に参照しているので、私はこの答えが好きです。ここでの答えはどれも、その特定の点に実際には触れていません。ありがとう。
Talha Sayed 2014

2

アセンブリプログラミングを学ぶと、データ、スタック、コードなどのセグメントを切り分ける必要があることがわかります。データセグメントは、文字列と数値が存在する場所です。コードセグメントは、コードが存在する場所です。これらのセグメントは、実行可能プログラムに組み込まれています。もちろん、スタックサイズも重要です... スタックオーバーフローは必要ありません。

したがって、データセグメントが500バイトの場合、プログラムには500バイトの領域があります。データセグメントを1500バイトに変更すると、プログラムのサイズは1000バイト大きくなります。データは実際のプログラムに組み込まれます。

これは、高水準言語をコンパイルするときに起こっていることです。実際のデータ領域は、実行可能プログラムにコンパイルされるときに割り当てられ、プログラムのサイズが大きくなります。プログラムはその場でメモリを要求することもでき、これは動的メモリです。RAMからメモリを要求すると、CPUがメモリを使用できるようにして、手放すことができ、ガベージコレクターがメモリを解放してCPUに戻します。必要に応じて、優れたメモリマネージャによって、ハードディスクにスワップすることもできます。これらの機能は、高水準言語が提供するものです。


2

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

確かに、コンパイル時にメモリを割り当てることはできません。しかし、コンパイル時に実際に何が起こるか。

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

想像してみてください。メモリで実行されているプログラムはありません。これは大きな空の長方形で示しています。

空のフィールド

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

最初のインスタンス

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

2番目のインスタンス

そして三番目

3番目のインスタンス

などなど。

この視覚化がこの概念をよく説明しているといいのですが。


2
それらの図が静的メモリと動的メモリの違いを示している場合、それらはより有用なIMHOになります。
Bartek Banachewicz 2014

これは、物事をシンプルに保つために、私が意図的に避けていたものです。私の焦点は、技術的な混乱を招くことなく、このファンダを明確に説明することです。これが静的変数のためのものである限り、この点は以前の回答によって十分に確立されているので、私はこれをスキップしました。
user3258051 2014

1
ええと、この概念は特に複雑ではないので、なぜ必要以上に単純にするのかわかりませんが、それは補足的な回答としてのみ意図されているので、わかりました。
Bartek Banachewicz 2014

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.