c++ arithmetic-expressions

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í:

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)