为什么0x7FFFFFFFull | (1 << 31) 在 C++ 中返回 0xFFFFFFFFFFFFFFFF?

Why is 0x7FFFFFFFull | (1 << 31) returning 0xFFFFFFFFFFFFFFFF in C++?

当我执行 (0x7fffffff | 0x8000000) 时,我得到的是 0xffffffffffffffff 而不是预期的 0xffffffff。我错过了什么?

一些示例代码和输出来说明我的问题。

代码:

#include <iostream>

using namespace std;

int main()
{
        unsigned long long val = 0;

        for (int i = 0; i < 64; i++) {
                val |= 0x1 << i;
                cout << i << ": " << std::hex << val << std::dec << endl;
        }

        return 0;
}

输出:

0: 1
1: 3
2: 7
3: f
4: 1f
5: 3f
6: 7f
7: ff
8: 1ff
9: 3ff
10: 7ff
11: fff
12: 1fff
13: 3fff
14: 7fff
15: ffff
16: 1ffff
17: 3ffff
18: 7ffff
19: fffff
20: 1fffff
21: 3fffff
22: 7fffff
23: ffffff
24: 1ffffff
25: 3ffffff
26: 7ffffff
27: fffffff
28: 1fffffff
29: 3fffffff
30: 7fffffff
31: ffffffffffffffff
32: ffffffffffffffff
33: ffffffffffffffff
34: ffffffffffffffff
35: ffffffffffffffff
36: ffffffffffffffff
37: ffffffffffffffff
38: ffffffffffffffff
39: ffffffffffffffff
40: ffffffffffffffff
41: ffffffffffffffff
42: ffffffffffffffff
43: ffffffffffffffff
44: ffffffffffffffff
45: ffffffffffffffff
46: ffffffffffffffff
47: ffffffffffffffff
48: ffffffffffffffff
49: ffffffffffffffff
50: ffffffffffffffff
51: ffffffffffffffff
52: ffffffffffffffff
53: ffffffffffffffff
54: ffffffffffffffff
55: ffffffffffffffff
56: ffffffffffffffff
57: ffffffffffffffff
58: ffffffffffffffff
59: ffffffffffffffff
60: ffffffffffffffff
61: ffffffffffffffff
62: ffffffffffffffff
63: ffffffffffffffff

因为符号扩展 val 是一个 unsigned long long。但是 val 不是你要改变的。您正在移动 0x1,一个 int,然后将其扩展为 long long for the '|='

首先,您的代码没有按照您在 title/question 中所说的执行;我已经编辑了标题。

问题是1 << 31。如果您有 32 位 int(从结果来看,显然您有),这会导致算术溢出。在 C++14 中,这是 implementation-defined 行为;在 C++14 之前,它会导致未定义的行为。 Reference.

通常 implementation-defined 行为将生成设置了符号位且未设置其他位的 int。在 2 的补码中,该值为 INT_MIN。然后,您在 unsigned long longint 之间执行算术运算。这定义为:int 转换为 unsigned long long.

将有符号整数转换为无符号整数是通过将其环绕(模块化算术)模 ULLONG_MAX+1 来完成的。所以(unsigned long long)INT_MIN的结果是一个非常大的正数,实际上是0xFFFFFFFF80000000。 (要检查这一点,请向其中添加 0x80000000 以获得 0)。

所以你实际上是在做 0x7FFFFFFF | 0xFFFFFFFF80000000 这给出了观察到的结果。


稍后情况会变得更糟:1 << 32 和更大的字符会由于移动整个字体宽度而导致未定义的行为。

要解决此问题,请将代码中的 0x1 更改为 1ull。您将移动 unsigned long long,而不是 int