为何64位机器上unsigned long无法存储2^64?
问题解析:unsigned long 左移的那些坑
先给你拆解下你遇到的几个核心问题:
1. 为什么 1UL << 64 会触发警告?
C语言标准明确规定:对于任何整数类型,左移的位数必须严格小于该类型的位宽度。哪怕是无符号类型也不例外——当左移位数等于或超过类型的位宽度时,行为是未定义的(Undefined Behavior)。
你的系统里unsigned long是64位(因为long是8字节=64位,unsigned long和long位宽相同),所以最大的合法左移位数是63(从0开始计数的话,第63位是最高位)。1UL << 64违反了这个规则,gcc自然会抛出shift-count-overflow警告。
2. 为什么 1UL << 63 用%ld输出会得到负数?
这里的问题出在格式符不匹配:
1UL是unsigned long类型,左移63位后的值是2^63,这是unsigned long能表示的最大值的一半(64位无符号数范围是0到2^64-1)。- 但你用了
%ld格式符,这个格式符是用来输出有符号long类型的。当把unsigned long的2^63传给printf时,它会被当作有符号long来解析:在补码表示中,64位有符号long的最高位是符号位,2^63对应的二进制是最高位为1、其余为0,这正好是有符号long能表示的最小负数:-9223372036854775808。
如果要正确输出unsigned long类型的值,你应该用%lu格式符,试试这个:
printf("2^63 (unsigned): %lu\n", 1UL << 63);
这样输出就会是正确的9223372036854775808。
3. 关于unsigned long的位宽验证
你通过sizeof(long)得到8字节,确实说明long是64位,unsigned long自然也是64位(无符号版本只是改变了符号位的解读,位宽和对应的有符号类型一致)。你之前的疑惑只是因为位运算规则和格式符匹配的问题,并不是unsigned long的位宽不对。
总结一下:
- 左移位数不能等于或超过类型的位宽度,否则是未定义行为
- 输出无符号类型时一定要用对应的无符号格式符(
%lu对应unsigned long,%u对应unsigned int等)
内容的提问来源于stack exchange,提问作者papiro
相关产品推荐
相关产品推荐

