11

「コンパイラはメモリ内の変数にスペースを割り当てる」と本に書かれているのはなぜですか。それを行うのは実行可能ファイルではありませんか?たとえば、次のプログラムを作成すると、

#include <iostream>
using namespace std;

int main()
{
   int foo = 0;
   cout<<foo;
   return 0;
}

それをコンパイルし、実行可能ファイル (program.exe とする) を取得します。ここで、program.exe を実行すると、この実行可能ファイル自体が変数 foo にスペースを割り当てるようにコマンドを実行します。そうじゃない?本が「コンパイラはこれを行う...これを行う」と言い続ける理由を説明してください。実際には、コンパイルされた実行可能ファイルはそれを行います。

この質問に別の関連する質問を追加すると、なぜsizeofコンパイル時演算子と呼ばれるのですか? それは実際には実行時演算子ではありませんか?

4

7 に答える 7

11

技術的には、スペース自体を作成する行為は実行時に行われますが、コンパイラーは、変数のために、ケースでスタックに予約するスペースを把握するものです。foo

コンパイラはint型のサイズを認識しているため、適切なアセンブラ命令を生成して、スタック上に十分なスペースを確保し、そこに存在させることができfooます。

あなたが示したプログラムに対して生成された以下のアセンブラ (MSVC2012 を使用) を見ると、何が起こるかを示すためにその一部にコメントを付けています。

#include "stdafx.h"
#include <iostream>
using namespace std;

int main()
{
//Setup stack frame for main by storing the stack pointer from the calling function and 
//reserving space for local variables and storing commonly used registers on the stack
002E4390  push        ebp  
002E4391  mov         ebp,esp  
// reserve space for local variables, which is 204 bytes here, no idea why so much.
// this is where the compiler calculated the size of your foo and added that to whatever else needs to be stored on the stack. Subtract from stack pointer (esp) because stack grows downward.  
002E4393  sub         esp,0CCh  
002E4399  push        ebx  
002E439A  push        esi  
002E439B  push        edi  
002E439C  lea         edi,[ebp-0CCh]  // load effective address of [ebp-0CCh], which I suspect would be your foo variable into edi register
002E43A2  mov         ecx,33h  
002E43A7  mov         eax,0CCCCCCCCh  
002E43AC  rep stos    dword ptr es:[edi]  //fill block of memory at es:[edi] with stuff  
   int foo;
   return 0;
002E43AE  xor         eax,eax  //set eax to zero for return value
}
// restore everything back to how it was before main was called
    002E43B0  pop         edi  
    002E43B1  pop         esi  
    002E43B2  pop         ebx  
    002E43B3  mov         esp,ebp  
    002E43B5  pop         ebp  
    002E43B6  ret  
于 2013-04-04T08:22:29.547 に答える
8

用語の使い方が緩いだけです。もちろん、コンパイラはプログラムにメモリを割り当てません。より正確な説明は、プログラムの実行時に割り当てるメモリの量をランタイムに伝えることです。

プログラムが実際に実行されるまで、プログラムはメモリ内にないため (動的にロードされる場合を除きますが、実行時に発生するため、コンパイラの範囲外です)、メモリについて話す必要はありません。

それらの本が話しているのはcin >> x; int * y = new[x];、サイズが不明な動的割り当てとは対照的に、コンパイル時にサイズがわかっている変数を割り当てることです。

于 2013-04-04T08:21:58.510 に答える
3

それは、コンパイラがメモリ内の変数にスペースを割り当てることを示していますnew/malloc.

于 2013-04-04T08:21:49.323 に答える
2

もちろん、コンパイラは「変数にスペースを割り当てる」わけではありません。コンパイラは、メモリ内の変数にスペースを割り当てるコードを生成します。

つまり、持っている場合

int foo;
foo = 1;

ソースコードでは、コンパイラは次のようなコードを生成する場合があります

int* fooPtr = allocate sizeof(int)
*fooPtr = 1;

x86 アーキテクチャでは、通常、それallocateは単一のアセンブリ命令になります。

sub esp, 4    ; allocate 4 == sizeof(int) bytes on stack
              ; now the value of "esp" is equal to the address of "foo",
              ; i.e. it's "fooPtr"
mov [esp], 1  ; *fooPtr = 1

複数のローカル変数がある場合、コンパイラはそれらを構造体にパックしてまとめて割り当てます。

int foo;
int bar;
bar = 1;

としてコンパイルされます

struct Variables { int foo; int bar; };
Variables* v = allocate sizeof(Variables);
v->bar = 1;

また

sub esp, 4+4       ; allocate sizeof(Variables) on stack
mov [esp + 4], 1   ; where 4 is offsetof(Variables, bar)
于 2013-04-18T11:04:51.683 に答える
0

コンパイラは機械語命令を生成し、ローカル変数が占有するメモリ アドレスを決定します。各ローカル変数には、スタックのトップからの相対アドレスが与えられます。たとえばfoo、メモリ address にあると想定されますstack_pointer。変数がある場合、foo2それはアドレスに配置されますstack_pointer + 4。ここで、4 はint.

ローカル変数fooがアクセスされると、コンパイラは に保持されているアドレスを置き換えますstack_pointerstack_pointerハードウェアには、常に現在のスタックの一番上を指す特別なレジスタがあります。

コンパイラは、structまたはclass宣言を見て、メモリ内でどのように配置されているかを判断する責任があるため、各変数のサイズを認識しています。Sosizeofはコンパイル時に認識され、定数式として扱われます。のようなプリミティブ型intは、特定のサイズ (たとえばsizeof(int)4) であることが知られています。

于 2016-05-13T08:35:33.053 に答える
-1

コンパイラの構造を読むことをお勧めします。ストレージ フェーズに注目すると、クエリが解決されます。

プログラムがコンパイルされると、アセンブリ言語コードであるオブジェクト コードに変換されます。高水準言語プログラムのすべての行は、多くのアセンブリ言語ステップに変換されます。変換されたプログラムは、実行されるアセンブラに入れられます。マシン オペレーション テーブル (アセンブリ レベルでの MOT 命令) に変換されるコミラー構築には、STORAGE 割り当てフェーズがあります。これは、変数/レジスタのスペース割り当てが行われる場所です。C++ にも register 修飾子キーワードがあります。

于 2014-03-27T10:06:33.790 に答える