r值存储在c中的哪里?

时间:2009-11-03 04:49:33

标签: c rvalue

在C中,我有这段代码:

int a;
a = 10 + 5 - 3

我想问一下:(10 + 5-3)存放在哪里? (据我所知,a位于堆栈上,(10+5-3)怎么样?如何计算这个右值?)

7 个答案:

答案 0 :(得分:5)

常量可能在编译时被简化,因此您提出的问题可能无济于事。但是,确实需要在运行时从某些变量计算的i - j + k,可以“存储”在编译器喜欢的任何地方,具体取决于CPU架构:编译器通常会尝试尽力使用寄存器,例如

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

计算这样的表达式将其“存储”在累加器寄存器AX中,然后将其分配给具有STORE AX, dest等的某个存储器位置。如果一个现代的优化编译器在一个甚至半体面的CPU架构上(是的,包含x86! - )需要将寄存器溢出到内存中以获得任何相当简单的表达式,我会感到非常惊讶!

答案 1 :(得分:4)

通常,r值“存储”在程序本身内。

换句话说,编译器本身(在程序运行之前 )计算10 + 5 - 3值(它可以这样做,因为它全部基于常量立即值),并且它发出汇编代码以将此计算的结果存储在分配的任何l值中(在这种情况下,名为a的变量,编译器可能知道该变量是数据段原点的相对地址)。 / p>

因此只能在程序的二进制文件中找到值为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值最终都会实现,至少在某种程度上是通过一些设置的指令来实现的,这样无论是计算(运行时)还是立即计算出的正确值都能得到正确处理。

答案 2 :(得分:1)

这取决于编译器。通常,值(12)将由编译器计算。然后将其存储在代码中,通常作为加载/移动立即汇编指令的一部分。

答案 3 :(得分:1)

它存储的地方实际上完全直到编译器。该标准没有规定这种行为。

通过实际编译代码并查看汇编程序输出,可以看到典型的位置:

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类型的指令中。

请注意,它只是这么简单,因为表达式是一个常量值。一旦引入非常量值(如变量),代码就会变得有点复杂。那是因为你必须在内存中查找这些变量(或者它们可能已经在寄存器中),然后在运行时而不是编译时中操作值。

关于编译器如何计算值应该是什么,这与表达式评估有关,并且是另一个问题: - )

答案 4 :(得分:0)

  • RHS(右侧)中的计算结果由编译器在称为“常量传播”的步骤中计算。
  • 然后,它被存储为汇编指令的操作数,将值移动到a

这是MSVC的反汇编:

  int a;
  a = 10 + 5 - 3;

0041338E  mov         dword ptr [a],0Ch 

答案 5 :(得分:0)

您的问题基于错误的前提。

C中 lvalue 的定义属性是它在存储中有一个位置,即存储。这是左值与 rvalue 的区别。 Rvalue 存储在任何地方。这就是它成为右值的原因。如果它被存储,根据定义它将是 lvalue

答案 6 :(得分:0)

术语“左值”和“右值”用于将表达式世界二等分。也就是说,(10+5-3)是恰好是右值的表达式(因为你不能将&amp;运算符应用于它 - 在C ++中,规则更复杂)。在运行时,没有表达式,左值或右值。特别是,它们不会存储在任何地方。

您想知道值12的存储位置,但值12既不是左值也不是右值(与表达式12相反,后者将是右值,但12不会出现在你的程序中。)