コンパイル時に割り当てられるメモリとは、コンパイラがコンパイル時に解決し、プロセスメモリマップ内に特定のものが割り当てられることを意味します。
たとえば、グローバル配列について考えてみます。
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です。