pero - ¿Por qué c=++(a+b) da error de compilación?
error 5 en c++ (8)
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 .
A partir de esto, supongo que da un error de compilación porque
(a+b)
es un entero temporal y, por lo tanto, no es modificable.
¿Es correcto este entendimiento? Esta fue la primera vez que traté de investigar un problema, así que si hubiera algo que debería haber buscado, por favor avise.
(a + b) evalúa a un valor de r, que no puede incrementarse.
++ 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 las reglas de las convenciones de programación en C para facilitar la programación. Eso es.
Creo que mayormente respondiste tu propia pregunta. Podría hacer un pequeño cambio en su fraseo y reemplazar "variable temporal" con "rvalue" como mencionó C. Gibones.
Los términos variable, argumento, variable temporal, etc., se volverán más claros a medida que aprendas sobre el modelo de memoria de C (esto parece una buena descripción general: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).
El término "valor" puede parecer opaco cuando está comenzando, así que espero que lo siguiente ayude a desarrollar una intuición al respecto.
Lvalue / rvalue se refiere a los diferentes lados de un signo igual (operador de asignación): lvalue = lado izquierdo (L minúscula, no "uno") 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 los trazos generales de pincel , el compilador crea una lista de instrucciones en lenguaje de máquina que computan el resultado de una expresión (el rvalor) y luego coloca ese resultado en algún lugar (el lvalor). Imagina un compilador que trata con el siguiente fragmento de código:
x = y * 3
En el pseudocódigo de ensamblaje podría verse algo como 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 "lugar" para modificar, esencialmente cualquier cosa que pueda funcionar como un lvalor.
Comprender el modelo de memoria C será útil porque tendrá una mejor idea de cómo pasan los argumentos a las funciones y (eventualmente) cómo trabajar con la asignación de memoria dinámica, como la función malloc (). Por razones similares, podría estudiar un poco la programación de ensamblajes en algún momento para tener una mejor idea de lo que está haciendo el compilador. Además, si está utilizando gcc , la opción -S "Detener después de la fase de compilación propiamente dicha; no armar". puede ser interesante (aunque recomendaría probarlo en un fragmento de código pequeño ).
Como un aparte: la instrucción ++ ha existido desde 1969 (aunque comenzó en el predecesor de C, B):
(Ken Thompson) observó (era) que la traducción de ++ x era más pequeña que la de x = x + 1 ".
Seguir esa referencia de wikipedia lo llevará a una interesante reseña de Dennis Ritchie (la "R" en "K&R C") sobre la historia del lenguaje C, que se encuentra aquí para su conveniencia: http://www.bell-labs.com/usr/dmr/www/chist.html donde puede buscar "++".
Cuando se realiza la 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);
El estándar C11 establece en la sección 6.5.3.1.
El operando del operador de incremento o decremento de prefijo tendrá un tipo de puntero real o no calificado, atómico, calificado o no cualificado, y será un valor lime modificable
Y el "valor 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 vacío) que potencialmente designa un objeto; si un lvalue no designa un objeto cuando se evalúa, el comportamiento no está definido. Cuando se dice que un objeto tiene un tipo en particular, el valor es especificado por el valor de l utilizado para designar el objeto. Un lvalue modificable es un lvalue que no tiene tipo de matriz, no tiene un tipo incompleto, no tiene un tipo cualificado por const, y si es una estructura o unión, no tiene ningún miembro (incluido, recursivamente, cualquier miembro o elemento de todos los agregados o uniones contenidos) con un tipo const-calificado.
Por lo tanto,
(a+b)
no es un lvalor modificable y, por lo tanto, no es elegible para el operador de incremento de prefijo.
Es solo una regla, eso es todo, y posiblemente existe para (1) facilitar la escritura de compiladores de C y (2) nadie ha convencido al comité de estándares de C para que se relaje.
Hablando de manera informal, solo 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 pueda producir un temporal en el que
++
pueda operar, y el resultado es el valor de la expresión
++(a + b)
.
Estás en lo correcto.
the
++
intenta asignar el nuevo valor a la variable original.
Así que
++a
tomará el valor de
a
, le agregará
1
y luego lo asignará de nuevo a
a
.
Como, como dijiste, (a + b) es un valor temporal, y no una variable con una dirección de memoria asignada, la asignación no se puede realizar.
La razón es que el estándar requiere que el operando sea un lvalue.
La expresión
(a+b)
no es un lvalor, por lo que no se permite aplicar el operador de incremento.
Ahora, uno podría decir "OK, esa es la razón, pero en realidad no existe una" verdadera "razón" , pero, desafortunadamente, la redacción particular de cómo funciona el operador de hecho requiere que ese sea el caso.
La expresión ++ E es equivalente a (E + = 1).
Obviamente, no puede escribir
E += 1
si
E
no es un valor l.
Lo que es una vergüenza porque uno podría haber dicho:
"incrementa E en uno"
y listo.
En ese caso, la aplicación del operador en un valor que no sea de valor sería (en principio) perfectamente posible, a costa de hacer el compilador un poco más complejo.
Ahora, la definición podría volver a redactarse de manera trivial (creo que ni siquiera es originalmente C sino una reliquia de B), pero hacerlo cambiaría fundamentalmente el lenguaje a algo que ya no es 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 considera C ++ además de C (la pregunta está etiquetada como C, pero hubo una discusión sobre las sobrecargas de los 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 ser algo que no se puede incrementar en absoluto, o el incremento podría tener efectos secundarios muy considerables (no solo agregar 1 ).
El compilador debe ser capaz de hacer frente a eso y diagnosticar los casos problemáticos a medida que ocurren.
En un lvalor, todavía es algo trivial para verificar.
No es así para cualquier tipo de expresión casual dentro de un paréntesis que le arrojas a los pobres.
Esta no es una razón
real
por la que no se
pudo
hacer, pero sin duda es una explicación por la cual las personas que implementaron esto no están precisamente extasiadas al agregar una característica que promete muy poco beneficio para muy pocas personas.