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

7 minutos de lectura

avatar de usuario
Miguel

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 un +se pretendía.

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

  • Usos: stackoverflow.com/a/3182557/962089 Además, se imprime (o se usa) el valor integral de un tipo de carácter: std::cout << +c; Si esto sucede mucho, static_cast se pone muy desordenado.

    – chris

    20 mayo 2015 a las 22:17


  • Pista: ¿y si hubieras tenido la intención de escribir un signo menos?

    – imallet

    21 de mayo de 2015 a las 1:05

  • ¿Qué pasa si el tipo de e?

    – Shafik Yaghmour

    21 de mayo de 2015 a las 2:15

  • Que hace 2 × (3 × 4 × +5) hacer en la aritmética normal?

    – Jon Hanna

    21 de mayo de 2015 a las 16:06

  • @Boann Creo que la pregunta no es tan trivial como todo eso. No todas las “notaciones matemáticas básicas” funcionan en la programación. Todos sabemos que pensar en términos matemáticos cuando se programa es una receta para el desastre.

    – Atsby

    21 mayo 2015 a las 20:42


avatar de usuario
Brian Bi

los + se interpreta como un operador más unario. Simplemente devuelve el valor promocionado de su operando.

  • Para obtener más detalles sobre “promocionado”, consulte aquí

    –MM

    20 mayo 2015 a las 22:31

  • El efecto de esto es idéntico a int a = b * (c * d * (+e))

    – Cort Amón

    21 de mayo de 2015 a las 3:08

avatar de usuario
Andreas DM

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

  • El “valor positivo” es engañoso. Eso hace que parezca que devuelve el valor absoluto del operando, lo cual no es el caso.

    – Chris Hayes

    21 de mayo de 2015 a las 3:39

  • Ni tampoco - necesariamente devolver “el valor negativo”: int b = -5; std::cout << -b;

    – MSalters

    21 de mayo de 2015 a las 9:14

  • @ChrisHayes respuesta corregida, gracias

    – Andreas MD

    21 de mayo de 2015 a las 17:44

  • @MSalters gracias, corrigió la redacción

    – Andreas MD

    21 mayo 2015 a las 17:47

avatar de usuario
Shafik Yaghmour

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

Asumiendo e es un tipo de enumeración integral o sin ámbito terminaría teniendo las promociones integrales aplicadas de todos modos ya que * aplica el conversiones aritméticas habituales 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á un tipo aritmético, enumeración sin ámbito o 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 tratan en el apartado 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 inferior a En t entonces estaría cubierto por el párrafo 1:

Un prvalue de tipo entero que no sea bool, char16_t, char32_t o wchar_t cuyo rango de conversión de entero (4.13) sea menor que el rango de int se puede convertir en un prvalue de tipo int si int puede representar todos los valores del tipo fuente ; de lo contrario, el prvalue de origen se puede convertir en un prvalue de tipo int sin signo.

Para un conjunto completo de casos podemos mirar preferencia cp.

Unary plus también puede ser útil en algunos casos para resolver la ambigüedad, un caso interesante sería Resolver la sobrecarga ambigua en el puntero de función y std::function 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 notar que unario más se agregó a C99 para la simetría con unario menos, de la Justificación de la Norma Internacional—Lenguajes de Programación—C:

Unary plus fue adoptado por el Comité C89 a partir de varias implementaciones, por simetría con unary minus.

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 de lambda que cito arriba, usando unario más 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 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 de C++ anotado(BRAZO) tiene el siguiente comentario:

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

avatar de usuario
Dyrandz Famador

Como han explicado, (+) y (-) solo se usaron como operador unario:

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

entonces 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

avatar de usuario
salman a

¿Por qué compila? 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)

Considerando 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”.

avatar de usuario
ALAN WARD

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

por lo que podemos identificar posiciones de memoria individuales -20(%ebp) como variable b, hasta -8(%ebp) como variable e. -4(%epp) es la 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.

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 menos (-) y el más (+) solo indican si el número es positivo o negativo.

  • no es así, qué tal esto: int a = -5; int val = -a; //result val: 5

    – Dyrandz Famador

    21 de mayo de 2015 a las 12:59


  • --5 se convierte 4 :pags -(-5) = 5… es broma, sé que es solo un error tipográfico … sí, tienes razón 🙂 +1

    – Dyrandz Famador

    21 mayo 2015 a las 13:35


  • —5 está mal formado porque 5 es un prvalue.

    – LF

    13 de agosto de 2019 a las 12:42

¿Ha sido útil esta solución?