¿Por qué c = ++(a+b) da error de compilación?


Después de investigar, leí que el operador de incremento requiere que el operando tenga un objeto de datos modificable: https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

De esto supongo que da error de compilación porque (a+b) es un entero temporal y por lo tanto no es modificable.

¿Es correcto este entendimiento? Esta fue mi primera vez tratando de investigar un problema, así que si había algo que debería haber buscado, por favor, aconseje.

 107
Author: BartoszKP, 2018-06-20

8 answers

Es solo una regla, eso es todo, y posiblemente esté ahí para (1) hacer más fácil escribir compiladores de C y (2) nadie ha convencido al comité de estándares de C para relajarlo.

Informalmente hablando solo se puede escribir ++foo si foo puede aparecer en el lado izquierdo de una expresión de asignación como foo = bar. Como no puedes escribir a + b = bar, tampoco puedes escribir ++(a + b).

No hay ninguna razón real por la que a + b no podría producir un temporal sobre el que ++ puede operar, y el resultado de eso es el valor de la expresión ++(a + b).

 117
Author: Bathsheba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 20:52:25

El estándar C11 establece en la sección 6.5.3.1

El operando del operador de incremento o decremento del prefijo deberá tener tipo atómico, calificado o no calificado real o puntero, y será un lvalue modificable

Y el "lvalue modificable" se describe en la sección 6.3.2.1 subsección 1

Un lvalue es una expresión (con un tipo de objeto distinto de void) que potencialmente designa un objeto; si un lvalue no designa un objeto cuando es evaluado, el comportamiento es indefinido. Cuando un objeto se dice que tiene un tipo particular, el tipo es especificado por el lvalue utilizado para designar el objeto. A modificable lvalue es un lvalue que no tiene tipo array, no tiene un tipo incompleto, no tiene un tipo const calificado, y si es una estructura o sindicato, no tiene ningún miembro (incluyendo, recursivamente, cualquier miembro o elemento de todos los contenidos agregados o unions) with a const-qualified type.

So (a+b) no es un lvalue modificable y, por lo tanto, no es elegible para el operador de incremento de prefijo.

 40
Author: Christian Gibbons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-01 03:34:39

Tienes razón. ++ intenta asignar el nuevo valor a la variable original. Así que ++a tomará el valor de a, agrega 1 y luego lo asigna de nuevo a a. Dado que, como ha dicho, (a+b) es un valor temporal, y no una variable con dirección de memoria asignada, la asignación no se puede realizar.

 21
Author: Roee Gavirel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 15:09:56

Creo que en su mayoría respondió a su propia pregunta. Podría hacer un pequeño cambio en su fraseo y reemplazar " variable temporal "por" rvalue " como mencionó C. Gibbons.

Los términos variable, argumento, variable temporal y así sucesivamente se volverán más claros a medida que aprenda sobre el modelo de memoria de C (esto parece una buena visión general: https://www.geeksforgeeks.org/memory-layout-of-c-program / ).

El término "rvalue" puede parecer opaco cuando estás empezando, así que espero que el seguir ayuda a desarrollar una intuición al respecto.

Lvalue / rvalue están hablando de los diferentes lados de un signo igual (operador de asignación): lvalue = lado izquierdo (L minúscula, no un") rvalue = lado derecho

Aprender un poco sobre cómo C usa la memoria (y los registros) será útil para ver por qué la distinción es importante. En brocha ancha, el compilador crea una lista de instrucciones en lenguaje máquina que calculan el resultado de un expresión (el rvalue) y luego pone ese resultado en algún lugar (el lvalue). Imagine un compilador que trata con el siguiente fragmento de código:

x = y * 3

En el seudocódigo de la asamblea podría parecerse a este ejemplo de juguete:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

El operador ++ (y su contraparte--) necesitan un "somewhere" para modificar, esencialmente cualquier cosa que pueda funcionar como lvalue.

Comprender el modelo de memoria C será útil porque obtendrá una mejor idea en su cabeza sobre cómo se pasan los argumentos a las funciones y (eventualmente) cómo trabajar con la asignación dinámica de memoria, como la función malloc (). Por razones similares, podría estudiar alguna programación de ensamblado simple en algún momento para tener una mejor idea de lo que está haciendo el compilador. También si está utilizando gcc, la opción -S "Detener después de la etapa de compilación propiamente dicha; no ensamblar."puede ser interesante (aunque recomiendo probarlo en un fragmento de código pequeño).

Solo como un aparte: La instrucción ++ ha existido desde 1969 (aunque comenzó en el predecesor de C, B):

(Ken Thompon) observó que la traducción de ++x era menor que la de x=x+1."

La traducción es análoga a la compilación aquí, pero están hablando de una pieza inusual de hardware, un PDP-7. Siguiendo esa referencia de wikipedia te llevará a una interesante reseña de Dennis Ritchie (la "R" en "K&R C") sobre la historia de la C idioma, enlazado aquí por conveniencia: http://www.bell-labs.com/usr/dmr/www/chist.html donde puede buscar"++".

 12
Author: jgreve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 15:55:54

La razón es que el estándar requiere que el operando sea un lvalue. La expresión (a+b) no es un lvalue, por lo que no se permite aplicar el operador increment.

Ahora, uno podría decir "OK, esa es de hecho la razón, pero en realidad no hay otra razón *real* que no sea esa", pero desafortunadamente la redacción particular de cómo funciona el operador de hecho requiere que sea el caso.

La expresión ++E es equivalente a (E+=1).

Obviamente, no se puede escribir E += 1 si E no es un lvalue. Lo cual es una vergüenza porque uno podría haber dicho: "aumenta E por uno" y se hace. En ese caso, aplicar el operador en un no-lvalue sería (en principio) perfectamente posible, a expensas de hacer que el compilador sea un poco más complejo.

Ahora, la definición podría reformularse trivialmente (creo que ni siquiera es originalmente C sino una reliquia de B), pero hacerlo fundamentalmente cambie el idioma a algo que ya no sea compatible con sus versiones anteriores. Dado que el posible beneficio es bastante pequeño, pero las posibles implicaciones son enormes, eso nunca sucedió y probablemente nunca sucederá.

Si se considera C++ además de C (la pregunta se etiqueta C, pero hubo una discusión sobre las sobrecargas de operadores), la historia se vuelve aún más complicada. En C, es difícil imaginar que este podría ser el caso, pero en C++ el resultado de (a+b) podría muy bien ser algo que no se puede incrementar en absoluto, o aumentar podría tener efectos secundarios muy considerables (no solo agregar 1). El compilador debe ser capaz de lidiar con eso, y diagnosticar casos problemáticos a medida que ocurren. En un lvalue, eso sigue siendo un poco trivial para comprobar. No es así para cualquier tipo de expresión fortuita dentro de un paréntesis que le arrojas a la pobre.
Esta no es una verdadera razón por la que no podría hacerse, pero seguro que sirve como una explicación de por qué las personas que implementado esto no son precisamente extáticos para añadir una característica que promete muy poco beneficio para muy pocas personas.

 6
Author: Damon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-13 10:32:16

Cuando se realiza una expresión ++(a+b), por ejemplo:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
 3
Author: Jeet Parikh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 06:02:32

(a+b) se evalúa a un rvalue, que no se puede incrementar.

 3
Author: Casper B. Hansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-27 17:07:30

++ intenta dar el valor a la variable original y como (a+b) es un valor temporal no puede realizar la operación. Y son básicamente reglas de las convenciones de programación en C para facilitar la programación. Eso es.

 3
Author: Babu Chandermani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 07:33:50