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

c assignment-operator evaluation

1165 просмотра

5 ответа

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

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

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

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

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

Автор: Jin Источник Размещён: 08.11.2019 11:00

Ответы (5)


27 плюса

Решение

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

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

- Значение является выражением (с типом объекта, кроме пустоты) , которые потенциально обозначает объект; если 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-й элемент массива.

Автор: R Sahu Размещён: 20.08.2016 07:14

15 плюса

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

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

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

Автор: tofro Размещён: 20.08.2016 07:16

4 плюса

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

Небольшое доказательство того, что в вашем простом примере gcc делает именно то, что нужно, а не больше:

код:

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

return x;
}

построить с отладкой

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    $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
Автор: Jean-François Fabre Размещён: 20.08.2016 07:17

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!
Автор: user1084944 Размещён: 21.08.2016 02:44

-1 плюса

Как еще

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в реестре.
  5. Сохранить значение z(5) по адресу y. (Нужно / можно оптимизировать и повторно использовать «5» с первого шага.)
  6. Загрузить адрес xв реестре.
  7. Сохранить значение y(5) по адресу x.
Автор: Eric Towers Размещён: 20.08.2016 07:49
Вопросы из категории :
32x32