c++ - ¿Qué hace el compilador aquí: int a=b*(c*d*+ e)?
arithmetic-expressions (8)
¿Por qué se compila?
Se compila porque
+
se analiza como operador unario más, no como operador de suma.
El compilador intenta analizar tanto como sea posible sin generar errores de sintaxis.
Así que esto:
d * + e
Se analiza como:
-
d
(operando) -
*
(operador de multiplicación) -
+
(operador unario más)-
e
(operando)
-
Mientras que esto:
d*++e;
Se analiza como:
-
d
(operando) -
*
(operador de multiplicación) -
++
(operador de incremento previo)-
e
(operando)
-
Además, esto:
d*+++e;
Se analiza como:
-
d
(operando) -
*
(operador de multiplicación) -
++
(operador de incremento previo)-
+
(operador unario más)-
e
(operando)
-
-
Tenga en cuenta que no crea un error de sintaxis, sino el error del compilador "LValue Requrired".
Esta pregunta ya tiene una respuesta aquí:
- ¿Qué hace el operador unario plus? 14 respuestas
Tuve un error extraño en mi programa, y después de algunas horas de depuración, encontré la siguiente línea muy estúpida:
int a = b * (c * d * + e)
Si no lo ve: Entre
d
escribí
* +
, donde solo se pretendía a
+
.
¿Por qué esto compila y qué significa realmente?
Como explicaron, (+) y (-) se usaron como operador unario:
Los operadores unarios actúan sobre un solo operando en una expresión
int value = 6;
int negativeInt = -5;
int positiveInt = +5;
cout << (value * negativeInt); // 6 * -5 = -30
cout << (value * positiveInt); // 6 * +5 = 30
cout << (value * - negativeInt); // 6 * -(-5) = 30
cout << (value * + negativeInt); // 6 * +(-5) = -30
cout << (value * - positiveInt); // 6 * -(+5) = -30
cout << (value * + positiveInt); // 6 * +(+5) = 30
así que de tu código:
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int a = b * (c * d * + e)
//result: 2 * (3 * 4 * (+5) ) = 120
El
+
se interpreta como un operador unario más.
Simplemente devuelve el valor
promoted
de su operando.
El operador + entre d y e se tratará como un operador + unario que determinará solo el signo de e. Entonces el compilador verá esta declaración de la siguiente manera:
int a = b*(c*d*e) ;
Esto es solo matemática básica. Por ejemplo:
5 * -4 = -20
5 * +4 = 5 * 4 = 20
-5 * -4 = 20
Negativo * Negativo = Positivo
Positivo * Negativo = Negativo
Positivo * Positivo = Positivo
Esta es la explicación más fácil que existe.
El signo menos (-) y más (+) solo indican si el número es positivo o negativo.
Esto se compila porque el
+
se interpreta como unario más, que realizará las promociones integrales en los tipos integrales o de enumeración y el resultado tendrá el tipo del operando promocionado.
Suponiendo que
e
es un tipo de enumeración integral o sin ámbito, terminaría teniendo las promociones integrales aplicadas de todos modos ya que
*
aplica las
conversiones aritméticas habituales
a sus operandos que terminan en las
promociones
integrales para los tipos integrales.
Del borrador del estándar C ++
5.3.1
[expr.unary.op]
:
El operando del operador unario + tendrá una aritmética, enumeración sin ámbito o tipo de puntero y el resultado es el valor del argumento. La promoción integral se realiza en operandos integrales o de enumeración. El tipo del resultado es el tipo del operando promocionado.
Las promociones integrales se cubren en la sección
4.5
[conv.prom]
y si las variables
e
son de un tipo distinto de
bool, char16_t, char32_t, or wchar_t
y tienen un rango de conversión menor que
int
, el párrafo
1
cubriría:
Un prvalor de un tipo entero distinto de bool, char16_t, char32_t o wchar_t cuyo rango de conversión de enteros (4.13) es menor que el rango de int puede convertirse a un prvalue de tipo int si int puede representar todos los valores del tipo fuente ; de lo contrario, el valor de origen puede convertirse en un valor de tipo unsigned int.
Para un conjunto completo de casos, podemos mirar cppreference .
Unary plus también puede ser útil en algunos casos para resolver la ambigüedad, un caso interesante sería Resolver una sobrecarga ambigua en el puntero de función y std :: function para una lambda usando + .
Tenga en cuenta que, para esas respuestas, que se refieren a valores unarios y negativos, esto es engañoso, como muestra este ejemplo:
#include <iostream>
int main()
{
unsigned x1 = 1 ;
std::cout << -x1 << std::endl ;
}
lo que resulta en:
4294967295
Véalo en vivo usando gcc en wandbox .
Es interesante observar que se agregó unary plus a C99 para la simetría con unary menos, de la Justificación del estándar internacional — Lenguajes de programación — C :
Unary plus fue adoptado por el Comité C89 de varias implementaciones, por simetría con unary minus.
y no puedo llegar a un buen caso en el que el casting no sea suficiente para lograr la misma promoción / conversión deseada. El ejemplo lambda que cito arriba, usando unary plus para forzar que una expresión lambda se convierta en un puntero de función:
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
podría lograrse utilizando un reparto explícito:
foo( static_cast<void (*)()>( [](){} ) );
y se podría argumentar que este código es mejor ya que la intención es explícita.
Vale la pena señalar que el Manual de referencia anotado de C ++ ( ARM ) tiene el siguiente comentario:
Unary plus es un accidente histórico y generalmente inútil.
Para dar un giro adicional a las respuestas correctas ya dadas aquí, si compila con el indicador -s, el compilador de C generará un archivo de ensamblaje en el que se pueden examinar las instrucciones reales generadas. Con el siguiente código C:
int b=1, c=2, d=3, e=4;
int a = b * (c * d * + e);
El ensamblado generado (usando gcc, compilando para amd64) comienza con:
movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl $3, -12(%ebp)
movl $4, -8(%ebp)
entonces podemos identificar posiciones de memoria individuales -20 (% ebp) como variable b, hasta -8 (% ebp) como variable e. -4 (% epp) es variable a. Ahora, el cálculo se representa como:
movl -16(%ebp), %eax
imull -12(%ebp), %eax
imull -8(%ebp), %eax
imull -20(%ebp), %eax
movl %eax, -4(%ebp)
Entonces, como han comentado otras personas que respondieron, el compilador simplemente trata "+ e" como la operación positiva unaria. La primera instrucción movl coloca el contenido de la variable e en el registro del acumulador EAX, que luego se multiplica rápidamente por el contenido de la variable d o -12 (% ebp), etc.
Unary
+
devuelve el valor promocionado.
Unario
-
devuelve la negación:
int a = 5;
int b = 6;
unsigned int c = 3;
std::cout << (a * +b); // = 30
std::cout << (a * -b); // = -30
std::cout << (1 * -c); // = 4294967293 (2^32 - 3)