大负整数字面量

14

在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的错误?

编辑:正如几位用户指出的那样,不存在负整数字面量。请参阅Keith Thompson下面的出色答案以获取更多详细信息。


有趣的是,这里有反汇编代码:cout << -4294967293 << '\n'; push 0Ah mov esi, esp push 3 注意它立即将3推入堆栈。(VS2010 ultimate) - ScarletAmaranth
std::cout 的流操作符可能不会按你所期望的那样提升这些字面值。 - AJG85
@ScarletAmaranth 我认为这没问题,因为4294967293首先被读取为无符号整数,然后被否定,得到的是3。不过我不太确定。 - Gunther Piez
@drhirsch:他没有使用 numeric_limits<int>::min(),而是使用硬编码字面量,这将是编译器默认使用的任何内容。 - AJG85
他在注释中编写了 numeric_limits,以明确编译器使用的内容,而且他的值是正确的。 - Gunther Piez
显示剩余2条评论
2个回答

18

-2147483648,例如,不是整数字面量。它是由一元运算符-应用于字面量2147483648的表达式。

在C++2011标准之前,C++不需要存在任何大于32位的类型(C++2011添加了至少64位的long long),因此字面量2147483648是不可移植的。

十进制整数字面量是以下第一个适合其值的类型:

int
long int
long long int (C++ 2011 and later)

请注意,在标准C++中,它从未是无符号类型。在C++标准的2011年之前的版本(没有long long int),一个十进制整数字面量如果太大而无法适应long int,则会导致未定义的行为 - 但您不太可能需要担心这个问题。在C++2011及以后的版本中,如果十进制整数字面量不适合long long int,则程序将是“非法的”。
一些非常旧的g ++版本没有实现C++2011语义。例如,g ++ 4.6.1(我基于原始版本回答此问题的版本)将文字2147483648视为unsigned long类型的常量。(有关更多过时信息,请参见此问题的编辑历史记录。)
以下是一个小程序,您可以使用它来显示编译器如何处理文字:
#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";
}

当我使用 g++ 11.3.0 在 64 位 Ubuntu 上编译并执行它时,我得到:

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

2
但是,正如您刚才解释的那样,整数字面量永远不会是无符号的。在这里,2147483648 应该具有 long long 类型。 - Alan Stokes
@user763305:在VS2010中输出相同。 - Keith Thompson
@KeithThompson: 如果在编译命令行中指定 "-std=c++11" 或 "-std=gnu++11" 呢?"-std=c99" 对于类似的 C 代码有帮助。 - Alexey Frunze
@Alex:g++ 4.5.2不支持“-std=c++11”,只支持“-std=c++0x”。更新的gcc版本可能会支持它;我会启动一个更新的系统并尝试一下。 - Keith Thompson
@Alex:我刚刚用gcc 4.6.1尝试了一下,结果相同。它仍然没有“-std=c++2011”选项,但使用“-std=c++0x”时具有相同的行为。 - Keith Thompson
显示剩余4条评论

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都允许我们编写这些字面量,并将它们视为无符号标记。这解释了输出内容的行为,使我相信输出结果是正确的(假设我们在数字上放置了“u”后缀)。我仍然觉得有趣的是,“-2147483648”不是有效的有符号整数字面量,尽管它是一个有效的有符号整数值。有任何想法吗?

5
-2147483648根本不是有符号整数字面值。2147483648是一个整数字面值(实际上是常量),而“-”是一元减号运算符。 - James McNellis
可能是因为它被读作 2147483648,这个数只存在于无符号整数中,然后被取反了。 - Gunther Piez
@James:谢谢你的提醒。我之前不知道“-”不是字面量的一部分。 - Ken Wayne VanderLinde
尝试在编译命令行中指定“-std=c++11”或“-std=gnu++11”以进行C ++代码,或使用“-std=c99”进行C代码。这应该有所帮助(对于C语言肯定有帮助,但我的C ++编译器太旧无法验证)。 - Alexey Frunze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接