relacionales operadores operaciones asignacion aritmeticos c assignment-operator evaluation

operaciones - operadores de asignacion en c++



¿Cuál es el punto de evaluar el operando izquierdo del operador de asignación en C? (5)

Según la norma ISO C11 - 6.5.16.3, dice que

  1. Un operador de asignación almacena un valor en el objeto designado por el operando izquierdo. Una expresión de asignación tiene el valor del operando izquierdo después de la asignación, pero no es un valor l. El tipo de una expresión de asignación es el tipo que tendría el operando izquierdo después de la conversión de lvalue. El efecto secundario de actualizar el valor almacenado del operando izquierdo se secuencia después de los cálculos de valor de los operandos izquierdo y derecho. Las evaluaciones de los operandos no son posteriores.

Así que supongo que esto significa que, por ejemplo,

int x = 10; x = 5 + 10;

  1. El operando izquierdo x se evalúa a 10 y el operando derecho se evalúa a 15.
  2. El valor del operando derecho se almacena en el objeto designado por el operando izquierdo x .

Pero si el propósito de la asignación es almacenar el valor evalado del operando derecho (como en el paso 2), ¿por qué es necesaria la evaluación del operando izquierdo? ¿Cuál es el punto de evaluar el operando izquierdo?


Como si no

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

¿trabajo? (La asignación " z=5 " debe dar el valor (r-) de z a la asignación " y= ... ", que luego debe dar el valor de y a la asignación " x= ... ". )

El comportamiento bajo el capó es:

  1. Cargue el valor 5 en un registro (y no reutilice este registro para nada más hasta el paso 7, a continuación)
  2. Cargar la dirección de z en un registro (Esto es lo que significa " z " cuando se usa como valor l.)
  3. Almacenar 5 en la dirección de z . 5 es ahora el valor de " z ". Recuerde que las CPU funcionan con valores y direcciones, no con " z ". La etiqueta variable " z " es un referente amigable para el ser humano a una dirección de memoria que contiene un valor. Dependiendo de cómo se use, desearemos su valor (cuando obtengamos el valor de z ) o su dirección (cuando reemplazemos el valor de z ).
  4. Cargar la dirección de y en un registro.
  5. Almacene el valor de z (5) en la dirección de y . (Uno debería / podría optimizar y reutilizar el "5" desde el primer paso.)
  6. Cargue la dirección de x en un registro.
  7. Almacene el valor de y (5) en la dirección de x .

Cuando x se evalúa como un lvalue , no se evalúa como 10. Se evalúa como un lvalue donde se puede almacenar el valor de RHS. Si el LHS no se evalúa como un valor de lvalue , la declaración sería un error.

De la norma C99 (6.3.2.1/1):

Un lvalue es una expresión (con un tipo de objeto distinto de vacío) que potencialmente designa un objeto; Si un valor de l no designa un objeto cuando se evalúa, el comportamiento no está definido.

La evaluación de la LHS como un valor lime es trivial cuando tiene una variable simple, como

x = 10;

Sin embargo, puede ser más complejo.

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

Si getIndex() devuelve 5 , entonces el LHS se evalúa como un valor de l que designa el 7º elemento de la matriz.


Solo para convencerme (si no lo he hecho) desde el punto de vista de "Judas", lo que justifica que mi publicación solo responda a la pregunta simple en su caso simple.

pequeña prueba que muestra que en su ejemplo simple gcc hace exactamente lo que necesita, no más:

código:

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

construir con depuración

K:/jff/data/python//c>gcc -g -std=c11 -c assign.c

objdump con código C / asm entremezclado

K:/jff/data/python//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

Como se indica en las otras respuestas (agradables), no está dispuesto a parafrasear, pero si la expresión es más compleja, la dirección para almacenar el valor debe calcularse, por lo que es necesaria una evaluación de algún tipo.

EDITAR:

Con un código un poco más complejo:

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

Desmontaje:

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


Tiene expresiones no triviales en el lado izquierdo de = que deben evaluarse todo el tiempo. Aquí hay unos ejemplos.

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!


Un "operando de la izquierda" puede ser mucho más complicado que su simple x en su ejemplo (lo que, ciertamente, no es un desafío para evaluar):

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

Definitivamente necesita un poco de evaluación en el LHS. Su oración citada se refiere a lo que debe hacerse en el lado izquierdo de la tarea para encontrar el valor apropiado para recibir la tarea.