C语言中相同初值的无符号长整型位右移后结果为何不同?
差异产生的核心原因
这段代码的观测结果,本质是位宽不匹配、隐式类型转换、printf格式符截断三个因素叠加制造的错觉,和有符号/无符号移位的补位规则没有关系——毕竟两个变量都是unsigned long,适用的右移规则完全一致。
- 首先是第一个认知偏差:默认移位前a和b的完整值完全相等,实际上二者从存储位模式上从一开始就不一样
主流64位桌面/服务器系统上(x86_64架构),unsigned long的实际宽度是64位,不是直觉上的32位:- 初始化
b = 0xFFFFFFFF时,字面量0xFFFFFFFF是32位宽度的无符号整数,赋值给64位的unsigned long时会自动做零扩展,最终b的实际存储值是0x00000000FFFFFFFF——高32位全是0,只有低32位是全1 a = ~a是对初始值为全0的64位a做按位取反,最终a的实际存储值是0xFFFFFFFFFFFFFFFF——64位全是1,和b的高32位状态完全不同
- 初始化
- 其次是第二个认知偏差:以为printf打印的是变量的完整值
代码里用的%x格式符,对应的是输出32位宽度的unsigned int类型值,读取可变参数时只会截取传入的64位变量的低32位内容打印。因为a和b的低32位在右移前确实都是全1,截断显示的结果就会让人误以为两个变量的完整值完全相等。 - 最后是右移环节的实际运算
两个变量都是无符号类型,执行的都是逻辑右移(高位补0),规则完全统一:- 64位全1的a右移1位后,实际完整值为
0x7FFFFFFFFFFFFFFF,打印时截取低32位输出,就会得到a = 0xFFFFFFFF的结果 - 高32位为0的b右移1位后,实际完整值为
0x000000007FFFFFFF,打印时截取低32位输出,就会得到b = 0x7FFFFFFF的结果
- 64位全1的a右移1位后,实际完整值为
快速验证方法
只要把printf里的%x替换成%lx(对应unsigned long宽度的十六进制输出格式),就能直接打印出两个变量的完整64位值,立刻就能看到二者从赋值完成开始就不是相等的。如果在32位系统上编译运行这段代码,因为unsigned long是32位宽度,两个变量移位后的结果会完全一致,也能反向印证位宽不匹配是差异的核心来源。
内容的提问来源于stack exchange,提问作者Zhaoxi ZHOU
相关产品推荐
相关产品推荐

