Вопрос

в C у меня есть этот фрагмент кода:

int a;
a = 10 + 5 - 3

Я хочу спросить:где хранится (10+5-3)?(Насколько я знаю, a находится в стеке, как насчет (10+5-3)?Как рассчитывается это значение?)

Это было полезно?

Решение

Обычно значение r «хранится» внутри самой программы.

Другими словами, сам компилятор (прежде чем программа будет запущена) вычисляет значение 10 + 5 - 3 (он может это сделать, поскольку все оно основано на постоянных непосредственных значениях) и генерирует ассемблерный код для сохранения результата этого вычисления в любом l-значении для присваивания (в этом В данном случае это переменная с именем a, которая, вероятно, известна компилятору как относительный адрес своего рода источника сегмента данных).

Таким образом, значение r, равное 12, можно найти только внутри двоичного файла программы, в инструкции ассемблера, которая выглядит нравиться

  mov <some dest, typically DS-relative>, $0C 

$0C — это «r-значение».

Если значение r оказалось результатом вычисления, которое можно выполнить только во время выполнения, скажем, если базовый код C был:а = 17*х;// x некоторая переменная времени выполнения, значение r тоже будет «сохранено» (или, скорее, материализовано) как серия инструкций в двоичном файле программы.Разница с простым «mov dest, imm», приведенным выше, заключается в том, что потребуется несколько инструкций, чтобы загрузить переменную x в аккумулятор, умножить ее на 17 и сохранить результат по адресу, где находится переменная a.Вполне возможно, что компилятор может «авторизоваться» ;-) на использование стека для какого-то промежуточного результата и т.д.но это было бы так
а) полностью зависит от компилятора
б) транзиторный
в) и обычно включает только часть значения r
поэтому можно с уверенностью сказать, что значение r — это концепция времени компиляции, которая инкапсулируется в частях программы (а не в данных) и не хранится нигде, кроме двоичного файла программы.

В ответ paxdiablo:объяснение, предложенное выше, действительно ограничивает возможности, поскольку стандарт c эффективно делает нет диктовать что-либо в этом роде.Тем не менее, большая часть любого r-значения в конечном итоге материализуется, по крайней мере частично, с помощью некоторых инструкций, которые настраивают все таким образом, чтобы правильное значение, рассчитанное (во время выполнения) или немедленное, было правильно адресовано.

Другие советы

Константы, вероятно, упрощаются во время компиляции, поэтому ваш буквально заданный вопрос может не помочь.Но что-то вроде, скажем, i - j + k который необходимо вычислить во время выполнения из некоторых переменных, может быть «сохранен» где угодно компилятору, в зависимости от архитектуры ЦП:компилятор обычно старается изо всех сил использовать регистры, например.

 LOAD AX, i
 SUB AX, j
 ADD AX, k

вычислить такое выражение, «сохранив» его в аккумуляторном регистре AX, прежде чем присвоить его некоторой ячейке памяти с помощью STORE AX, dest или т.п.Я был бы очень удивлен, если бы современному оптимизирующему компилятору даже на полуприличной архитектуре ЦП (да, включая x86!-) потребовалось бы загружать регистры в память для любого достаточно простого выражения!

Это зависит от компилятора.Обычно значение (12) рассчитывается компилятором.Затем он сохраняется в коде, обычно как часть инструкции немедленной сборки загрузки/перемещения.

Где он хранится, на самом деле полностью вплоть до компилятора.Стандарт не предписывает такое поведение.

А типичный место можно увидеть, скомпилировав код и посмотрев выходные данные ассемблера:

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 тип инструкции.

Обратите внимание, что это все так просто, потому что выражение является постоянным значением.Как только вы вводите непостоянные значения (например, переменные), код становится немного сложнее.Это потому, что вам нужно искать эти переменные в памяти (или они могут уже находиться в регистре), а затем манипулировать значениями в время выполнения, нет время компиляции.

Что касается того, как компилятор вычисляет, каким должно быть значение, это связано с оценкой выражения и это совсем другой вопрос :-)

  • Результат вычислений в правой части (правая часть) вычисляется компилятором на этапе, который называется «постоянное распространение».
  • Затем оно сохраняется как операнд инструкции ассемблера, перемещая значение в a

Вот дизассемблирование MSVC:

  int a;
  a = 10 + 5 - 3;

0041338E  mov         dword ptr [a],0Ch 

Ваш вопрос основан на неверной предпосылке.

Определяющее свойство lvalue в C заключается в том, что у него есть место в памяти, т.е. это хранится.Это то, что отличает lvalue от rvalue.Rvalue это нет хранится где угодно.Вот что делает его ценным.Если бы оно хранилось, оно было бы lvalue по определению.

Термины «lvalue» и «rvalue» используются для разделения мира выражений пополам.То есть, (10+5-3) — это выражение, которое является rvalue (поскольку к нему нельзя применить оператор & — в C++ правила более сложны).Во время выполнения нет выражений, lvalue или rvalue.В частности, они нигде не хранятся.

Вам интересно, где хранится значение 12, но значение 12 не является ни lvalue, ни rvalue (в отличие от выражения 12 что было бы значением r, но 12 не отображается в вашей программе).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top