为何C语言中左移24位会改变unsigned long的取值?
C语言中无符号长整型移位赋值与字面量比较不符的问题解析
问题现象
原本预期0b11010010 << 24与0b11010010000000000000000000000000取值相同,但将结果存入unsigned long变量后,变量比较结果为0;直接使用字面量比较时结果为1。给字面量添加UL后缀后,问题得到解决。测试环境为Apple clang 14.0.0,目标架构x86_64。
原测试代码
#include <stdbool.h> #include <stdint.h> #include <stdio.h> int main(){ unsigned long a = 0b11010010000000000000000000000000; unsigned long b = 0b11010010 << 24; bool isTheSame1 = a == b; printf("isTheSame1 %d \n",isTheSame1); bool isTheSame2 = 0b11010010000000000000000000000000 == (0b11010010 << 24); printf("isTheSame2 %d",isTheSame2); }
原输出结果
isTheSame1 0 isTheSame2 1
原因分析
问题核心在于C语言字面量的默认类型和有符号整数溢出的行为:
- 字面量默认类型:二进制字面量
0b11010010和0b11010010000000000000000000000000默认被编译器解析为signed int类型(x86_64平台下int为32位)。 - 移位操作的溢出问题:
0b11010010作为32位有符号int,值为210,左移24位后得到0xD2000000,该值超过了32位有符号int的最大值(2^31-1=2147483647),触发有符号整数溢出,结果变为负数(十进制-771754880)。- 将这个负数赋值给64位
unsigned long时,会按照无符号整数规则转换为一个极大的64位值(18446744072937796736)。
- 直接赋值的类型转换:
0b11010010000000000000000000000000作为32位有符号int是负数,但赋值给unsigned long时,会被解释为无符号的32位值0xD2000000,扩展为64位后是3523215360,与移位转换后的结果完全不同。 - 字面量直接比较的特殊性:直接比较时,两个操作数都是32位有符号int,值均为
0xD2000000(作为负数相等),所以比较结果为1。
解决方案
给所有参与移位运算和赋值的二进制字面量添加UL后缀,明确指定其为unsigned long类型,避免有符号整数溢出和类型转换带来的不一致问题。
更新后的测试代码
#include <stdbool.h> #include <stdint.h> #include <stdio.h> int main(){ unsigned long a = 0b11010010000000000000000000000000UL; unsigned long b = 0b11010010UL << 24; bool isTheSame1 = a == b; printf("isTheSame1 %d \n",isTheSame1); bool isTheSame2 = 0b11010010000000000000000000000000UL == (0b11010010UL << 24); printf("isTheSame2 %d",isTheSame2); }
更新后的输出结果
isTheSame1 1 isTheSame2 1
内容的提问来源于stack exchange,提问作者JasonHsieh
相关产品推荐
相关产品推荐

