You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移位操作无截断还原的异常问题咨询

为什么long类型左移n位再右移n位后结果和原数相等?

这问题挺贴合实际编程中的认知误区,我来拆解一下背后的原因:

1. 编译器对移位位数的“取模”处理

在你用的多数主流编译器(比如GCC、Clang、MSVC)中,当你对一个k位的整数类型执行移位操作时,如果移位的位数n大于等于k,编译器会自动将n对k取模,实际执行的是n % k位的移位。

比如你代码里的long,在多数系统上是64位的(k=64)。当你设置n=128时,128 % 64 = 0,所以:

  • b = a << 128 等价于 b = a << 0,也就是b直接等于a
  • 再执行c = b >> 128,等价于c = b >> 0,结果还是b(也就是a)

自然最后a == c会返回true,这不是因为没有截断,而是实际根本没执行有效的移位操作。

2. 标准中的“未定义行为” vs 编译器的实现逻辑

这里要注意:C/C++标准并没有规定移位位数大于等于类型比特宽度时的行为,这属于未定义行为——理论上编译器可以做任何处理。但为了实用性,多数编译器都选择了“取模”的处理方式,这才导致了你看到的结果。

3. 如何验证你的“截断”认知?

如果想看到移位导致的截断和数据丢失,你需要让移位的位数小于类型的比特宽度,比如针对64位long:

long a = 0x123456789ABCDEF0;
long n = 32;
long b = a << n;  // 左移32位,低32位被移出截断,高32位补0
long c = b >> n;  // 右移32位,高32位补符号位(这里a是正数,补0)
printf("a: %lx\n", a);   // 输出 123456789abcdef0
printf("b: %lx\n", b);   // 输出 9abcdef000000000
printf("c: %lx\n", c);   // 输出 000000009abcdef0
// 此时a != c,能明显看到低32位的数据丢失了

如果是负数(有符号数的算术右移),数据丢失的效果会更直观:

long a = 0x8000000000000000;  // 64位long的最小负数
long n = 1;
long b = a << 1;  // 左移1位,最高位移出后补0,结果变成0
long c = b >> 1;  // 右移1位还是0,此时a != c,能看到原始数据完全丢失

4. 你的原始认知修正

你之前认为“超出缓冲区大小的数值会被截断,且永久丢失”是对的,但前提是移位的位数是有效的(小于类型比特宽度)。当移位位数大于等于类型宽度时,编译器的取模处理让实际移位位数变成了0或者小于宽度的值,这才导致了看起来没有截断的错觉。


内容的提问来源于stack exchange,提问作者theRobotized

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:42:00