2011-12-14 15 views
10

En Visual Studio 2010, el siguiente programagrandes literales enteros negativos

#include <iostream> 
using std::cout; 

int main() 
{ 
    cout << -2147483646 << '\n'; 
    cout << -2147483647 << '\n'; 
    cout << -2147483648 << '\n'; // numeric_limits<int>::min() 
    cout << -2147483649 << '\n'; 
    cout << -2147483650 << '\n'; 
    cout << "..." << '\n'; 
    cout << -4294967293 << '\n'; 
    cout << -4294967294 << '\n'; 
    cout << -4294967295 << '\n'; // -numeric_limits<unsigned int>::max() 
    cout << -4294967296 << '\n'; 
    cout << -4294967297 << '\n'; 
} 

genera la siguiente salida

-2147483646 
-2147483647 
2147483648 
2147483647 
2147483646 
... 
3 
2 
1 
-4294967296 
-4294967297 

¿Qué está pasando?

¿Es este comportamiento estándar o un error de Visual Studio?

Editar: Como varias personas han señalado, no hay tal cosa como un literal entero negativo . Vea la excelente respuesta de Keith Thompson a continuación para obtener más detalles.

+0

Interesante, aquí está el desmontaje: cout << -4294967293 << '\ n'; push 0Ah mov es, esp push 3 Observe que empuja 3 como inmediato de inmediato. (VS2010 ultimate) – ScarletAmaranth

+0

El operador de flujo de 'std :: cout' probablemente no esté promocionando esos literales como era de esperar. – AJG85

+0

@ScarletAmaranth Creo que está bien, porque 4294967293 se lee primero como unsigned int y que negated, que rinde 3. No estoy seguro aunque – hirschhornsalz

Respuesta

16

-2147483648, por ejemplo, no es un literal entero; es una expresión que consiste en un operador único - aplicado al literal 2147483648.

Antes del nuevo estándar C++ 2011, C++ no requiere la existencia de ningún tipo más grande que 32 bits (C++ 2011 agrega long long), por lo que el literal 2147483648 no es portátil.

un entero decimal literal es del primero de los siguientes tipos en el que su valor adapta a:

int 
long int 
long long int (new in C++ 2011) 

Nota que nunca es de un tipo sin signo en la norma C++. En las versiones de 1998 y 2003 del estándar C (que no tienen long long int), un literal entero decimal que es demasiado grande para caber en long int da como resultado un comportamiento indefinido. En C++ 2011, si un literal entero decimal no cabe en long long int, entonces el programa está "mal formado".

Pero gcc (al menos a partir del release 4.6.1, el último que tengo) no implementa la semántica de C++ 2011. El literal 2147483648, que no cabe en una longitud de 32 bits, se trata como unsigned long, al menos en mi sistema de 32 bits. (Eso está bien para C++ 98 o C++ 2003; el comportamiento no está definido, por lo que el compilador puede hacer lo que quiera).)

Así dado un típico de 32 bits de complemento a 2 int tipo, esto:

cout << -2147483647 << '\n'; 

toma el valor int2147483647, lo niega, e imprime el resultado, que coincide con el resultado matemático que cabría esperar . Pero esto:

cout << -2147483648 << '\n'; 

(cuando se compila con gcc 4.6.1) toma el valor long o unsigned long2147483648, niega que como un unsigned int, produciendo 2147483648, e imprime eso.

Como han mencionado otros, puede usar sufijos para forzar un tipo particular.

Aquí es un pequeño programa que se puede utilizar para mostrar cómo sus compilador trata literales:

#include <iostream> 
#include <climits> 

const char *type_of(int)    { return "int"; } 
const char *type_of(unsigned int)  { return "unsigned int"; } 
const char *type_of(long)    { return "long"; } 
const char *type_of(unsigned long)  { return "unsigned long"; } 
const char *type_of(long long)   { return "long long"; } 
const char *type_of(unsigned long long) { return "unsigned long long"; } 

int main() 
{ 
    std::cout << "int: " << INT_MIN << " .. " << INT_MAX << "\n"; 
    std::cout << "long: " << LONG_MIN << " .. " << LONG_MAX << "\n"; 
    std::cout << "long long: " << LLONG_MIN << " .. " << LLONG_MAX << "\n"; 

    std::cout << "2147483647 is of type " << type_of(2147483647) << "\n"; 
    std::cout << "2147483648 is of type " << type_of(2147483648) << "\n"; 
    std::cout << "-2147483647 is of type " << type_of(-2147483647) << "\n"; 
    std::cout << "-2147483648 is of type " << type_of(-2147483648) << "\n"; 
} 

Cuando compilo, consigo algunas advertencias:

lits.cpp:18:5: warning: this decimal constant is unsigned only in ISO C90 
lits.cpp:20:5: warning: this decimal constant is unsigned only in ISO C90 

y la siguiente salida, incluso con gcc -std=c++0x:

int: -2147483648 .. 2147483647 
long: -2147483648 .. 2147483647 
long long: -9223372036854775808 .. 9223372036854775807 
2147483647 is of type int 
2147483648 is of type unsigned long 
-2147483647 is of type int 
-2147483648 is of type unsigned long 

me da la misma salida con VS2010, al menos con defau Configuraciones

+2

Pero, como acabas de explicar, un literal entero nunca está sin firmar. 2147483648 debería tener el tipo 'long long' aquí. –

+0

@Keith: Usted se está contradiciendo a sí mismo: "Tenga en cuenta que nunca es de un tipo sin firmar" y "toma el valor int no firmado 2147483648". ¿Podrías aclararlo por favor? – user763305

+1

¡OOPS! Lo arreglaré. –

2

Cuando compilo esto en GCC, me sale el siguiente mensaje:

warning: this decimal constant is unsigned only in ISO C90 [enabled by default] 

Se produce por cada línea después (e incluyendo)

cout << -2147483648 << '\n'; // numeric_limits<int>::min() 

Así que lo que está sucediendo es compilador de Visual Studio y GCC nos permite escribir estos literales, y solo los tratan como si estuvieran marcados como sin firmar. Esto explica el comportamiento de lo que se imprime, y me hace bastante seguro de que la salida es correcta (suponiendo que hayamos colocado un sufijo u en los números).

Todavía me parece interesante que -2147483648 no es un literal entero con signo válido, a pesar de que es un valor entero con signo válido. ¿Alguna idea sobre ese alguien?

+3

'-2147483648' no es un literal de entero con signo. '2147483648' es un entero literal (constante, realmente), y' -' es el operador unario negativo. –

+0

Posiblemente porque se lee como '2147483648', que existe solo como sin signo y luego se niega. – hirschhornsalz

+0

@James: gracias por no. No sabía que '-' no formaba parte del literal –

Cuestiones relacionadas