我对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。
答案 0 :(得分:9)
转换一个有符号整数,使1进入符号位位置甚至更进一步在C中未定义,因此编译器可以自由执行以下操作:
0x1 << 32
这里编译器看到一个32位的int(0x1),它被移位了32位。由于编译器可以以与更正确的移位一致的方式自由解释它,因此它将其解释为0x1_0000_0000
并尝试将其转换为32位int,从而生成0x0000_0000
,然后看到您稍后将结果分配给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
答案 1 :(得分:5)
0x1
的类型为int
,在具有32位int
的实现中,两个表达式都是评估:
0x1 << 32
和
0x1 << 31
调用未定义的行为。
要解决此问题(但假设您不想将zero
对象评估为0
),请执行KarolS回答
(size_t) 1 << 32
和
(size_t) 1 << 31
这假定size_t
类型宽于32位,这是x64上clang实现的情况。