在C中评估赋值运算符的左操作数有什么意义?

时间:2016-08-20 07:09:53

标签: c assignment-operator evaluation

根据ISO C11 - 6.5.16.3,它说

  
      
  1. 赋值运算符将值存储在指定的对象中   左操作数。赋值表达式的值为left   赋值后的操作数,但不是左值。的类型   赋值表达式是左操作数之后的类型   左值转换。更新存储值的副作用   左操作数在左和右的值计算之后排序   正确的操作数。对操作数的评估没有考虑。
  2.   

所以我猜这意味着,例如,

int x = 10;
x = 5 + 10;
  1. 左操作数x被评估为10,右操作数被评估为15.
  2. 右操作数值存储在左操作数x指定的对象中。
  3. 但是如果赋值的目的是存储右操作数的evalauted值(就像在step2中那样),为什么左操作数的评估是必要的?评估左操作数有什么意义?

5 个答案:

答案 0 :(得分:27)

x被评估为lvalue时,它不会评估为10.它评估为lvalue,其中可以存储RHS的值。如果LHS未评估为lvalue,则该语句将是错误。

根据C99标准(6.3.2.1/1):

  

左值是一个表达式(对象类型不是void),可能指定一个对象;如果左值在评估时未指定对象,则行为未定义。

当你有一个简单的变量时,将LHS评估为左值是微不足道的,例如

 x = 10;

然而,它可能更复杂。

 double array[10];
 int getIndex();   // Some function that can return an index based
                   // on other data and logic.

 array[getIndex()+1] = 10.0;

 // This looks like a function call that returns a value.
 // But, it still evaluates to a "storage area".
 int *getIndex2() { return(&array[0]); }
 *getIndex2()=123.45; // array[0]=123.45

如果getIndex()返回5,则LHS会评估指定数组第7个元素的左值

答案 1 :(得分:15)

A"左操作数"可能比你的例子中的简单x复杂得多(事实上,这并不是一个真正的评估挑战):

*(((unsigned long*)target)++) = longValue;

肯定需要对LHS进行一些评估。您引用的句子指的是在作业的左侧需要执行的操作,以便找到正确的左值来接收作业。

答案 2 :(得分:4)

只是为了说服自己(如果还没有完成)从“犹大”的角度来看,这证明我的帖子只能在你的简单案例中回答这个简单的问题。

小小的证明表明,在你的简单例子中,gcc只需要它,而不是更多:

代码:

int main()
{
int x = 10;
x = 5 + 10;

return x;
}

使用debug

构建
K:\jff\data\python\stackoverflow\c>gcc -g -std=c11 -c assign.c

使用混合C / asm代码的objdump

K:\jff\data\python\stackoverflow\c>objdump -d -S assign.o

assign.o:     file format pe-x86-64


Disassembly of section .text:

0000000000000000 <main>:
int main()
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 30             sub    $0x30,%rsp
   8:   e8 00 00 00 00          callq  d <main+0xd>
int x = 10;
   d:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
x = 5 + 10;
  14:   c7 45 fc 0f 00 00 00    movl   $0xf,-0x4(%rbp)

return x;
  1b:   8b 45 fc                mov    -0x4(%rbp),%eax
}
  1e:   90                      nop
  1f:   48 83 c4 30             add    $0x30,%rsp
  23:   5d                      pop    %rbp
  24:   c3                      retq
  25:   90                      nop
  26:   90                      nop
  27:   90                      nop
  28:   90                      nop
  29:   90                      nop
  2a:   90                      nop
  2b:   90                      nop
  2c:   90                      nop
  2d:   90                      nop
  2e:   90                      nop
  2f:   90                      nop

如其他(好的)答案中所述,不愿意解释,但如果表达式更复杂,则必须计算存储值的地址,因此需要进行某种评估。

编辑:

使用一些稍微复杂的代码:

int main()
{
int x[3];
int i = 2;
x[i] = 5 + 10;

return x[i];
}

拆卸:

Disassembly of section .text:

0000000000000000 <main>:
int main()
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 30             sub    $0x30,%rsp
   8:   e8 00 00 00 00          callq  d <main+0xd>
int x[3];
int i = 2;
   d:   c7 45 fc 02 00 00 00    movl   $0x2,-0x4(%rbp)
x[i] = 5 + 10;
  14:   8b 45 fc                mov    -0x4(%rbp),%eax  <== hey, could be more optimized here: movl   $0x2,%eax covers line+above line :)
  17:   48 98                   cltq
  19:   c7 44 85 f0 0f 00 00    movl   $0xf,-0x10(%rbp,%rax,4)  <== this line holds the left-operand evaluation, in a way, %rax is used to offset the array address
  20:   00

return x[i];
  21:   8b 45 fc                mov    -0x4(%rbp),%eax
  24:   48 98                   cltq
  26:   8b 44 85 f0             mov    -0x10(%rbp,%rax,4),%eax
}
  2a:   90                      nop
  2b:   48 83 c4 30             add    $0x30,%rsp
  2f:   5d                      pop    %rbp
  30:   c3                      retq

答案 3 :(得分:2)

=左侧有非常重要的表达式,需要一直进行评估。以下是一些例子。

int array[5];
int *ptr = malloc(sizeof(int) * 5);

*ptr = 1;      // The lhs needs to evaluate an indirection expression
array[0] = 5;  // The lhs needs to evaluate an array subscript expression

for (int i = 0; i < 5; ++i) {
    *ptr++ = array[i];  // Both indirection and postincrement on the lhs!
}

// Here, we want to select which array element to assign to!
int test = (array[4] == 0);
(test ? array[0] : array[1]) = 5; // Both conditional and subscripting!

答案 4 :(得分:-1)

怎么会

int x, y, z;
x = y = z = 5;

工作? (作业“z=5”必须将z的(r-)值赋予作业“y= ...”,然后必须将y的值赋予作业“x= ...”。)

引擎盖下的行为是:

  1. 在寄存器中加载值5(在下面的步骤7之前不要重复使用该寄存器)
  2. 在寄存器中加载z的地址(这是“z”在用作左值时的含义。)
  3. 将商店5存储在z的地址。 5现在是“z”的右值。请记住,CPU使用值和地址,而不是“z”。变量标签“z”是对包含值的内存地址的人类友好指示。根据它的使用方式,我们要么想要它的值(当我们获取z的值时)或它的地址(当我们替换z的值时)。
  4. 在注册表中加载y的地址。
  5. z(5)的值存储在y的地址。 (应该/可以优化并重用第一步中的“5”。)
  6. x的地址加载到注册表中。
  7. y(5)的值存储在x的地址。