4

size_t我对我注意到的一些行为感到困惑:

size_t zero = 0x1 << 32;
size_t big = 0x1 << 31;
size_t not_as_big = 0x1 << 30;
printf("0x1<<32: %zx\n0x1<<31: %zx\n0x1<<30: %zx\n", zero, big, not_as_big);

结果是:

0x1<<32: 0
0x1<<31: ffffffff80000000
0x1<<30: 40000000

现在,我知道这size_t只能保证至少是一个 16 位无符号整数,但我不明白为什么0x1<<31最终会得到它所做的值 - 尝试分配 18 艾字节在我的程序上做了一个数字。

我在 x86_64 上使用 LLVM。

4

2 回答 2

9

移动有符号整数以使 1 进入符号位位置,甚至在 C 中未定义,因此编译器可以自由地执行以下操作:

0x1 << 32

在这里,编译器看到一个 32 位 int (0x1),它被移动了 32 位。由于编译器可以自由地以与更正确的移位一致的方式对其进行解释,因此它将其解释为0x1_0000_0000并尝试将其转换为 32 位 int,从而得到0x0000_0000,然后看到您稍后将结果分配给 a size_t,通常是 64 位的:0x0000_0000_0000_0000

0x1 << 31

和以前一样,编译器可以自由地做任何看起来正确的事情,因为 1 位侵入了符号位的位置。所以结果是0x8000_0000,这是一个负数 -INT_MIN准确地说。然后,它看到您将该负数转换为 64 位,因此它使用 1 扩展它,就像所有负数一样。结果是0xffff_ffff_8000_0000,最小的 32 位有符号整数存储为有符号 64 位整数。

在所有 64 位平台之间正确且可移植的方法是:

((size_t)1) << 32
((size_t)1) << 31
于 2014-02-14T23:13:02.993 回答
5

0x1是类型int并且在 32-bit 的实现中int,两个表达式评估:

0x1 << 32

0x1 << 31

调用未定义的行为。

要解决此问题(但假设您不想让zero对象评估为0),请按照KarolS答案中的建议进行操作

(size_t) 1 << 32

(size_t) 1 << 31

这假定size_t类型比 32 位更宽,x64 上的 clang 实现就是这种情况。

于 2014-02-14T22:34:10.663 回答