Какова точка оценки левого операнда оператора присваивания в C?
В соответствии с ISO C11 - 6.5.16.3 говорится, что
- Оператор присваивания сохраняет значение в объекте, обозначенном левый операнд. Выражение присваивания имеет значение слева операнд после задания, но не является значением lvalue. Тип выражение присваивания - это тип, который будет иметь левый операнд после преобразование lvalue. Побочный эффект обновления сохраненного значения левый операнд секвенирован после вычисления значений слева и правые операнды. Оценки операндов не подвержены.
Итак, я думаю, это означает, что, например,
int x = 10;
x = 5 + 10;
- Левый операнд
x
оценивается до 10, а правый операнд оценивается равным 15.
- Значение правого операнда сохраняется в объекте, обозначенном левым операндом
x
.
Но если целью назначения является сохранение значения evalauted правильного операнда (как и на этапе 2), почему нужна оценка левого операнда? Какой смысл оценивать левый операнд?
Ответы
Ответ 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
.