Подтвердить что ты не робот

Какова точка оценки левого операнда оператора присваивания в C?

В соответствии с ISO C11 - 6.5.16.3 говорится, что

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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    $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

Ответ 4

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

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!

Ответ 5

Как еще

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

работать? (Назначение "z=5" должно присвоить (r-) значение z присвоению "y= ...", которое затем должно присвоить значение y присвоению "x= ...". )

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

  • Загрузите значение 5 в регистр (и не используйте его повторно для чего-либо еще до шага 7 ниже)
  • Адрес загрузки z в регистре (это означает, что "z" означает, когда он используется как lvalue.)
  • Сохранить 5 по адресу z. 5 теперь является значением r z ". Помните, что CPU работают со значениями и адресами, а не "z". Переменная метка "z" является дружественным к человеку референтом к адресу памяти, содержащему значение. В зависимости от того, как он используется, мы либо захотим его значения (когда мы получим значение z), либо его адрес (когда мы заменим значение z).
  • Загрузите адрес y в регистр.
  • Сохраняйте значение z (5) по адресу y. (Необходимо/может оптимизировать и повторно использовать "5" с первого шага.)
  • Загрузите адрес x в регистр.
  • Сохранить значение y (5) по адресу x.