Pregunta ¿Qué hace el compilador aquí: int a = b * (c * d * + e)? [duplicar]


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 ves: entre d y e escribí * +, donde solo una +se pretendía.

¿Por qué compila esto y qué significa realmente?


74
2018-05-20 21:58


origen


Respuestas:


los + se interpreta como un operador unario plus. Simplemente devuelve el promovido valor de su operando.


117
2018-05-20 21:59



Como Brian explicó, devuelve el valor promocionado.
Si fuera un - devolvería 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)

26
2018-05-20 22:16



Esto compila porque el + se está interpretando como unario más, que realizará las promociones integrales en tipos integrales o de enumeración y el resultado tendrá el tipo del operando promocionado.

Asumiendo e es un tipo de enumeración integral o no abarcado terminaría teniendo las promociones integrales aplicadas de todos modos desde * aplica el conversiones aritméticas usuales a sus operandos que termina en el promociones integrales para tipos integrales.

Del borrador del estándar C ++ 5.3.1  [expr.unary.op]:

El operando del operador unario + tendrá una enumeración aritmética, 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 de resultado es el tipo del operando promocionado.

Las promociones integrales están cubiertas en la sección 4.5  [conv.prom] y si las variables e es un tipo diferente a bool, char16_t, char32_t, or wchar_t y tienen un rango de conversión menor que En t entonces estaría cubierto por el párrafo 1:

Un prvalue de un tipo entero distinto de bool, char16_t, char32_t o wchar_t, cuya conversión entera   rango (4.13) es menor que el rango de int se puede convertir a un prvalue de tipo int si int puede representar todos   los valores del tipo de fuente; de lo contrario, el prvalue fuente se puede convertir a un valor prvalue de tipo unsigned   En t.

Para un conjunto completo de casos, podemos mirar cppreference.

Unary plus también puede ser útil en algunos casos para resolver ambigüedades, un caso interesante sería de Resolución de sobrecarga ambigua en el puntero de función y función std :: para una lambda usando +.

Tenga en cuenta, para esas respuestas, refiriéndose a unario - y valores 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 añadió una ventaja unaria a C99 para la simetría con menos unario, del  Justificación de los lenguajes de programación estándar internacional-C:

Unary plus fue adoptado por el Comité C89 de varias implementaciones, por simetría con menos unario.

y no puedo encontrar 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 unario más para forzar a una expresión lambda a convertirse en un puntero de función:

foo( +[](){} ); // not ambiguous (calls the function pointer overload)

podría lograrse usando un molde explícito:

foo( static_cast<void (*)()>( [](){} ) );

y podría argumentarse que este código es mejor ya que la intención es explícita.

Vale la pena señalar que Manual de referencia anotado de C ++ (BRAZO) tiene el siguiente comentario:

Unario plus es un accidente histórico y generalmente inútil.


18
2018-05-21 01:50



Como lo que explicaron, (+) y (-) solo se usaron como operador unario:

Operadores unarios actuar en 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 desde 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

9
2018-05-21 01:50



¿Por qué compila? Compila porque + se analiza como el operador unario plus, no como el 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 plus)
    • e (operando)

Mientras que, esto:

d*++e;

Se analiza como:

  • d (operando)
  • * (operador de multiplicación)
  • ++ (operador de incremento previo)
    • e (operando)

Por otra parte, esto:

d*+++e;

Se analiza como:

  • d (operando)
  • * (operador de multiplicación)
  • ++ (operador de incremento previo)
    • + (operador unario plus)
      • e (operando)

Tenga en cuenta que no crea un error de sintaxis sino el error del compilador "LValue requrired".


4
2018-05-21 12:30



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 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)

para que podamos 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 ha sido comentado por otras personas respondiendo, el compilador simplemente trata "+ e" como la operación positiva única. La primera instrucción movl coloca el contenido de la variable e en el registro del acumulador EAX, que luego se multiplica por los contenidos de la variable d o -12 (% ebp), etc.


4
2018-05-21 14:39



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 hay.

El menos (-) y el más (+) solo dicen si el número es positivo o negativo.


3
2018-05-21 10:16