где rvalue хранится в c?
В C, у меня есть этот кусок кода:
int a;
a = 10 + 5 - 3
Я хочу спросить: где хранится (10+5-3)?
(Насколько мне известно,a
находится в стеке, как о (10+5-3)
? Как рассчитывается это rvalue?)
7 ответов
Как правило, значение r "хранится" в самой программе.
другими словами, сам компилятор (перед запуском программы) вычисляет 10 + 5 - 3 value (он может сделать это, так как все это основано на постоянных немедленных значениях), и он выдает код сборки для хранения результата этого вычисления в любом l-значении для назначения (в этом случае переменная с именем a, которую компилятор, вероятно, знает как относительный адрес сегмента данных Происхождение видов).
значение r, которое имеет значение 12, поэтому находится только внутри двоичного файла программы, в инструкции по сборке, которая выглядит как
mov <some dest, typically DS-relative>, C
$0C - это "r-значение".
Если r-значение оказалось результатом вычисления, которое может быть выполнено только во время выполнения, скажем, если базовый код c был: a = 17 * x; / / x некоторое время выполнения var, r-значение тоже будет "сохранено" (или, скорее, материализовано) как серия инструкции в двоичном виде. Разница с простым "mov dest, imm" выше заключается в том, что потребуется несколько инструкций для загрузки переменной x в аккумулятор, умножить на 17 и сохранить результат по адресу, где находится переменная A. Возможно, что компилятор может "авторизоваться"; -) использовать стек для некоторого промежуточного результата и т. д. но такова была бы
a) полностью зависимый от компилятора
б) transiant
c) и, как правило, будет включать только часть значения r
поэтому можно с уверенностью сказать, что r-значение является концепцией времени компиляции, которая инкапсулируется в части программы (а не в данных) и хранится только в двоичном файле программы.
в ответ на paxdiablo: объяснение, предлагаемое выше, действительно ограничивает возможности, потому что стандарт c эффективно делает не диктовать что-нибудь в этом роде. Тем не менее, любой р-стоимость в конечном итоге материализован, по крайней мере частично, некоторыми инструкциями, которые настраивают вещи так, чтобы правильное значение, вычисленное (во время выполнения) или немедленное, было правильно адресовано.
константы, вероятно, упрощаются во время компиляции, поэтому ваш вопрос в буквальном смысле может не помочь. Но что-то вроде, скажем, i - j + k
что нужно вычислять во время выполнения из некоторых переменных, может быть "сохранено" там, где компилятор любит, в зависимости от архитектуры процессора: компилятор обычно пытается сделать все возможное, чтобы использовать регистры, например
LOAD AX, i
SUB AX, j
ADD AX, k
вычислить такое выражение, "храня" его в регистре накопителя AX, перед присвоением его некоторой памяти расположение с STORE AX, dest
или тому подобное. Я был бы очень удивлен, если бы современный оптимизирующий компилятор на даже полу-приличной архитектуре процессора (да, x86 включен!- ) необходимо разлить регистры в память для любого разумно простого выражения!
Это зависит от компилятора. Обычно значение (12)вычисляется компилятором. Затем он сохраняется в коде, как правило, как часть инструкции по загрузке/перемещению немедленной сборки.
, где он хранит это на самом деле полностью компилятор. Стандарт не диктует такого поведения.
A типичный место можно увидеть, фактически скомпилировав код и посмотрев на вывод ассемблера:
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 , %esp
andl $-16, %esp
movl , %eax
addl , %eax
addl , %eax
shrl , %eax
sall , %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
call __alloca
call ___main
movl , -4(%ebp) ;*****
movl , %eax
leave
ret
соответствующий бит пометкой ;*****
и вы можете видеть, что значение создается компилятором и просто вставляется непосредственно в mov
тип инструкция.
обратите внимание, что это только так просто, потому что выражение является постоянным значением. Как только вы вводите непостоянные значения (например, переменные), код становится немного сложнее. Это потому, что вы должны выглядеть эти переменные в памяти (или они уже могут быть в регистре), а затем манипулировать значениями в времени, а не времени компиляции.
что касается того, как компилятор вычисляет, каким должно быть значение, это нужно сделать с оценкой выражения и есть целый другой вопрос: -)
- результат вычисления в RHS (правая сторона) вычисляется компилятором на шаге, который называется "постоянное распространение".
- затем он сохраняется как операнд инструкции по сборке, перемещая значение в
a
вот разборка из MSVC:
int a;
a = 10 + 5 - 3;
0041338E mov dword ptr [a],0Ch
Ваш вопрос основан на неверной предпосылке.
определяющее свойство lvalue В C есть то, что у него есть место в хранилище, i.e это хранящиеся. Это то, что отличает lvalue от R-значения. Значение rvalue-это не хранить в любом месте. Это то, что делает его rvalue. Если бы он хранился, он был бы lvalue по определению.
термины "lvalue" и "rvalue" используются для разделения мира выражений. То есть, (10+5-3)
- Это выражение, которое является rvalue (потому что вы не можете применить к нему оператор & - в C++ правила сложнее). Во время выполнения нет выражений, lvalues или rvalues. В частности, они нигде не хранятся.
вам было интересно, где хранилось значение 12, но значение 12 не является ни lvalue, ни rvalue (в отличие от выражения 12
который был бы rvalue, но 12
не отображается в вашей программе).