В Visual Studio 2010 следующая программа
#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';
}
генерирует следующий вывод
-2147483646
-2147483647
2147483648
2147483647
2147483646
...
3
2
1
-4294967296
-4294967297
Что происходит?
Является ли это стандартным поведением или ошибкой Visual Studio?
Изменить: Как указывало несколько человек, нет такой вещи, как отрицательный целочисленный литерал. Более подробную информацию см. В обзоре Кит Томпсон ниже.
Ответ 1
-2147483648
, например, не является целым литералом; это выражение, состоящее из унарного оператора -
, примененного к литералу 2147483648
.
До нового стандарта С++ 2011 С++ не требует наличия какого-либо типа размером более 32 бит (С++ 2011 добавляет long long
), поэтому литерал 2147483648
не переносится.
Двойной целочисленный литерал имеет первый из следующих типов, в котором его значение соответствует:
int
long int
long long int (new in C++ 2011)
Обратите внимание, что он никогда не имеет тип unsigned в стандартном С++. В версиях стандарта C (1998) и версии 2003 (не имеющих long long int
) в версиях стандарта 1998 и 2003 годов, десятичный целочисленный литерал, который слишком велик, чтобы соответствовать long int
, приводит к поведению undefined. В С++ 2011, если десятичный целочисленный литерал не помещается в long long int
, тогда программа "плохо сформирована".
Но gcc (по крайней мере, начиная с версии 4.6.1, последняя из которых у меня есть) не реализует семантику С++ 2011. Литерал 2147483648
, который не подходит для 32-разрядной длины, рассматривается как unsigned long
, по крайней мере, на моей 32-битной системе. (Это хорошо для С++ 98 или С++ 2003, поведение undefined, поэтому компилятор может делать все, что ему нравится.)
Таким образом, для типичного 32-битного типа 2's-complement int
это:
cout << -2147483647 << '\n';
принимает значение int
2147483647
, отрицает его и печатает результат, который соответствует ожидаемому математическому результату. Но это:
cout << -2147483648 << '\n';
(при компиляции с gcc 4.6.1) принимает значение long
или unsigned long
2147483648
, отрицает его как неподписанный int, уступая 2147483648
, и печатает это.
Как уже упоминалось, вы можете использовать суффиксы для принудительного создания определенного типа.
Здесь небольшая программа, которую вы можете использовать, чтобы показать, как ваш компилятор обрабатывает литералы:
#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";
}
Когда я его компилирую, я получаю несколько предупреждений:
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
и следующий вывод, даже с 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
Я получаю тот же вывод с VS2010, по крайней мере, с настройками по умолчанию.
Ответ 2
Когда я компилирую это в GCC, я получаю следующее сообщение:
warning: this decimal constant is unsigned only in ISO C90 [enabled by default]
Это происходит для каждой строки после (и включая)
cout << -2147483648 << '\n'; // numeric_limits<int>::min()
Итак, что происходит, как компилятор Visual Studio, так и GCC позволяют нам писать эти литералы, и они просто рассматривают их так, как если бы они были помечены как unsigned. Это объясняет поведение печатаемого текста, и это делает меня довольно уверенным, что вывод правильный (предположим, что мы разместили суффикс u
на числах).
Мне все же интересно, что -2147483648
не является допустимым символом целых чисел со знаком, даже если он является допустимым знаковым целочисленным значением. Любая мысль о том, что кто-нибудь?