Cには、次のコードがあります:
int a;
a = 10 + 5 - 3
質問したいのですが、(10+5-3) はどこに保存されていますか? (私が知る限り、a
はスタックにありますが、どう(10+5-3)
ですか?この右辺値はどのように計算されますか?)
通常、r 値はプログラム自体に「格納」されます。
言い換えれば、コンパイラ自体 (プログラムが実行される前) は 10 + 5 - 3 の値を計算し (それはすべて定数即値に基づいているため、そうすることができます)、アセンブリ コードを出力して結果を格納します。代入の左辺値 (この場合、a という名前の変数。コンパイラはおそらく、データ セグメントの起点への相対アドレスとして認識します)。
したがって、値が 12 の r 値は、プログラムのバイナリ内で、次のようなアセンブリ命令内でのみ見つかります。
mov <some dest, typically DS-relative>, $0C
$0C は「r 値」です。
r 値がたまたま実行時にのみ実行できる計算の結果である場合、たとえば、基になる c コードが次の場合: a = 17 * x; // x ランタイム var の場合、r 値もプログラム バイナリ内の一連の命令として「格納」(または実体化) されます。上記の単純な「mov dest, imm」との違いは、変数 x をアキュムレータにロードし、17 を掛けて、変数 a が存在するアドレスに結果を格納するには、いくつかの命令が必要になることです。コンパイラが「それ自体を承認」する可能性があります;-) 中間結果などにスタックを使用することは可能ですが、これは
a) 完全にコンパイラに依存します
b) 一時的です c) 通常はr 値の一部
のみを含みます
したがって、r 値はコンパイル時の概念であり、プログラムの一部 (データではなく) にカプセル化され、プログラム バイナリ以外のどこにも格納されないと言っても過言ではありません。
paxdiablo への対応: 上記の説明は、実際には c 標準がその性質の何も指示しないため、可能性を制限するものです。それでもなお、ほとんどの r 値は、少なくとも部分的には、(実行時に) 計算された値であれ即時値であれ、適切な値が適切に処理されるように設定する命令によって、最終的に具体化されます。
定数はおそらくコンパイル時に単純化されるため、文字通り提起された質問は役に立たない場合があります。しかし、たとえば、i - j + k
実行時にいくつかの変数から計算する必要があるようなものは、CPU アーキテクチャに応じて、コンパイラが好きな場所に「保存」できます。コンパイラは通常、レジスタを使用するために最善を尽くします。
LOAD AX, i
SUB AX, j
ADD AX, k
このような式を計算して、アキュムレータ レジスタ AX に「格納」してから、などを使用してメモリ位置に割り当てますSTORE AX, dest
。ある程度まともな CPU アーキテクチャ (そう、x86 が含まれています!-) 上の最新の最適化コンパイラが、合理的に単純な式のためにレジスタをメモリにスピルする必要があるとしたら、私はかなり驚かれることでしょう!
これはコンパイラに依存します。通常、値 (12) はコンパイラによって計算されます。次に、通常はロード/ムーブ即時アセンブリ命令の一部として、コードに格納されます。
a
MSVC からの逆アセンブルは次のとおりです。
int a;
a = 10 + 5 - 3;
0041338E mov dword ptr [a],0Ch
格納する場所は、実際には完全にコンパイラ次第です。標準では、この動作は規定されていません。
典型的な場所は、実際にコードをコンパイルしてアセンブラーの出力を見ることで確認できます。
int main (int argc, char *argv[]) {
int a;
a = 10 + 5 - 3;
return 0;
}
これは以下を生成します:
.file "qq.c"
.def ___main;
.scl 2;
.type 32;
.endef
.text
.globl _main
.def _main;
.scl 2;
.type 32;
.endef
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
call __alloca
call ___main
movl $12, -4(%ebp) ;*****
movl $0, %eax
leave
ret
関連するビットがマークされており、値がコンパイラによって作成され、型命令;*****
に直接挿入されていることがわかります。mov
式が定数値であるため、これだけ単純であることに注意してください。非定数値 (変数など) を導入するとすぐに、コードはもう少し複雑になります。これは、これらの変数をメモリ内で調べて (または既にレジスタにある可能性があります)、コンパイル時ではなく実行時に値を操作する必要があるためです。
コンパイラが値をどのように計算するかについては、それは式の評価に関係しており、まったく別の問題です:-)
あなたの質問は間違った前提に基づいています。
C におけるlvalueの定義プロパティは、ストレージに場所があることです。つまり、それは格納されています。これが、 lvalue とrvalueの違いです。右辺値はどこにも保存されません。それがそれを右辺値にするものです。格納されている場合、定義により左辺値になります。
「左辺値」と「右辺値」という用語は、式の世界を二分するために使用されます。つまり、(10+5-3)
たまたま右辺値である式です (& 演算子を適用できないため、C++ では規則がより複雑になります)。実行時には、式、左辺値、または右辺値はありません。特に、それらはどこにも保存されません。
値 12 がどこに格納されているのか疑問に思っていましたが、値 12 は左辺値でも右辺値でもありません (12
右辺値であるが12
プログラムには表示されない式とは対照的に)。