Какой смысл оценивать левый операнд оператора присваивания в C?

согласно ISO C11-6.5.16.3, он говорит, что

  1. оператор присваивания сохраняет значение в объект, указанный левый операнд. Выражение присваивания имеет значение левого операнд после задания, но не является lvalue. Тип выражение присваивания-это тип, который будет иметь левый операнд после преобразование значения. Побочный эффект обновления сохраненного значения левый операнд секвенируется после значения вычисления левых и правые операнды. Оценки операндов не имеют последовательности.

Итак, я думаю, это означает, что, например,

int x = 10;
x = 5 + 10;
  1. левый операнд x оценивается в 10, а правый операнд - в 15.
  2. значение правого операнда хранится в объекте, обозначенном левым операндом x.

но если целью назначения является сохранение evalauted значения права операнд (как и на Шаге 2), почему необходима оценка левого операнда? Какой смысл оценивать левый операнд?

5 ответов


, когда x оценивается как lvalue, он не оценивает до 10. Он оценивает в lvalue где значение RHS можно хранить. Если LHS не оценивает до lvalue, заявление будет ошибкой.

из стандарта C99 (6.3.2.1/1):

An lvalue - это выражение (с типом объекта, отличным от void), которое потенциально обозначает объект; если lvalue не обозначает объект, когда он оценивается, поведение не определено.

оценка LHS как lvalue тривиально, когда у вас есть простая переменная, такая как

 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 оценивает в lvalue что обозначает 7-й элемент массива.


"левый операнд" может быть намного сложнее, чем ваш простой x в вашем примере (который, по общему признанию, не является проблемой для оценки):

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

определенно нуждается в некоторой оценке на LHS. Ваше цитируемое предложение относится к тому, что нужно сделать в левой части задания, чтобы найти правильный lvalue для получения задания.


просто чтобы убедить себя (если еще не сделано) с точки зрения "Иуды", что оправдывает то, что мой пост отвечает только на простой вопрос в вашем простом случае.

небольшое доказательство, показывающее, что в вашем простом примере 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

objdump с смешанным кодом C/asm

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    x30,%rsp
   8:   e8 00 00 00 00          callq  d <main+0xd>
int x = 10;
   d:   c7 45 fc 0a 00 00 00    movl   xa,-0x4(%rbp)
x = 5 + 10;
  14:   c7 45 fc 0f 00 00 00    movl   xf,-0x4(%rbp)

return x;
  1b:   8b 45 fc                mov    -0x4(%rbp),%eax
}
  1e:   90                      nop
  1f:   48 83 c4 30             add    x30,%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

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

EDIT:

С несколько более сложным кодом:

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    x30,%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   x2,-0x4(%rbp)
x[i] = 5 + 10;
  14:   8b 45 fc                mov    -0x4(%rbp),%eax  <== hey, could be more optimized here: movl   x2,%eax covers line+above line :)
  17:   48 98                   cltq
  19:   c7 44 85 f0 0f 00 00    movl   xf,-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    x30,%rsp
  2f:   5d                      pop    %rbp
  30:   c3                      retq

у вас есть нетривиальные выражения в левой части = это нужно оценивать все время. Вот несколько примеров.

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!

как бы

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

работы? (Задание"z=5 " должен дать (r-)значение z С назначением "y= ...", который затем должен дать значение y С назначением "x= ...".)

поведение под капотом:

  1. загрузить значение 5 в регистре (и не используйте этот регистр для чего-либо еще до шага 7 ниже)
  2. адрес загрузки z в регистре (это то, что"z" означает, когда он используется как lvalue.)
  3. магазин 5 по адресу z. 5 Теперь значение "z". Помните, что процессоры работают со значениями и адресами, а не"z". Метка переменной"z"-это дружественный референт к адресу памяти, который содержит значение. В зависимости от того, как он используется, мы либо хотим его значение (когда мы получим значение z) или его адрес (при замене значения z).
  4. адрес загрузки y в a реестр.
  5. сохранение z (5) по адресу y. (Нужно/можно оптимизировать и использовать "5" с первого шага.)
  6. загрузить адрес x в реестр.
  7. сохранение y (5) по адресу x.