1. ホーム
  2. c++

[解決済み] コンパイル時にメモリを確保する」の本当の意味とは?

2022-04-20 02:29:11

質問

C言語やC++などのプログラミング言語では、静的メモリ割り当てと動的メモリ割り当てについてよく言及されます。その概念は理解できるのですが、「すべてのメモリはコンパイル時に割り当てられた(予約された)」という表現にはいつも困惑させられます。

コンパイルは、私の理解では、高レベルのC/C++コードを機械語に変換し、実行ファイルを出力するものです。コンパイルされたファイルでは、メモリはどのように確保されるのでしょうか?メモリは常に仮想メモリ管理されたRAMに割り当てられるのではありませんか?

メモリ確保は、定義上、実行時の概念ではないのですか?

C/C++のコードで1KBの静的割り当て変数を作ったら、実行ファイルのサイズも同じだけ大きくなるのでしょうか?

これは、"Static allocation"という見出しで、このフレーズが使われているページの一つです。

バック・トゥ・ベーシックス メモリ割り当て、その歴史をたどる

どのように解決する?

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

例えば、グローバル配列について考えてみましょう。

int array[100];

コンパイラは,コンパイル時に配列の大きさを知っており,その大きさに応じて int そのため、コンパイル時に配列の全サイズを知ることができます。また、グローバル変数はデフォルトで静的記憶期間を持ちます。プロセスのメモリ空間(.data/.bssセクション)の静的メモリ領域に割り当てられているのです。この情報を元に コンパイラはコンパイル時に、その静的メモリ領域のどのアドレスに配列を配置するかを決定します。 .

もちろん、メモリアドレスは仮想アドレスです。プログラムは自分自身のメモリ空間全体(例えば0x00000000から0xFFFFFFFまで)を持っていると仮定しています。そのため、コンパイラは、「よし、配列は0x00A33211番地だ」というような仮定をすることができます。実行時には、このアドレスはMMUとOSによって実アドレス/ハードウェアアドレスに変換されます。

値が初期化されたスタティック・ストレージは、少し事情が異なります。例えば

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

最初の例では、コンパイラは配列がどこに配置されるかを決定し、その情報を実行ファイルに保存するだけでした。

値が初期化されるものの場合、コンパイラは配列の初期値も実行ファイルに注入し、プログラム開始時の配列割り当て後に、配列にこれらの値を入れるようにプログラムローダに指示するコードも追加しています。

このコンパイラ(GCC4.8.1、x86ターゲット)が生成したアセンブリの例を2つ紹介します。

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 というのも、標準規格では、静的に保存されたものはデフォルトでゼロに初期化されるべきとされているからです。

8.5.9(イニシャライザ)【注】。

静的ストレージの持続時間のオブジェクトは、すべてゼロ初期化されます。 他の初期化が行われる前に、プログラムの起動が行われます。場合によっては 場合によっては、後で追加の初期化が行われます。

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