为什么带变量的左移和带常量的左移会产生不同的结果?

why does left shift with variables generate different result from that with constant?

本文关键字:左移 结果 常量 变量 为什么      更新时间:2023-10-16

在编译了下面的代码之后,我得到了一个奇怪的结果,a =1而b =0。有人能解释一下幕后发生了什么吗?

#include<iostream>
using namespace std;
int main(){
    int n=32; 
    int a=1<<n;  //turns out a=1
    int b=1<<32; //turns out b=0
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
}

标准没有定义,或者更确切地说,它将其定义为"未定义的行为",当左移超过整数类型的大小时会发生什么。[这种未定义行为的原因是不同的硬件可能会或可能不会表现相同,例如,向左移动32位]。

在第一种情况下[至少没有优化],编译器生成指令来计算1 << 32 -在x86上变成1 << (32 & 31),与1 << 0相同-因此你得到1。

在第二种情况下,编译器将自己计算该值,这将变成溢出,并给出0。

很有可能(但不确定),如果您更改编译器选项来优化代码,它在两种情况下都给出零。如果你要做一个小位移的循环,你会得到你想要的行为(尽管你可能会发现"有趣"的行为与数字变成负的事实,所以最好对所有的移位操作使用无符号,真的)。

因为您正在调用未定义的行为。移动比类型中存在的更多的位没有定义为具有任何特定的行为。

见http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1

超大移位量:将uint32_t移位32位或更多未定义的。我猜这是因为潜在的不同cpu上的移位操作对这个:for做不同的事情例如,X86将32位的移位量截断为5位(因此移位由32位与移位0位相同,但PowerPC会截断32位移位等于6位(因此移位32位产生零)。由于这些硬件的差异,行为完全不同未定义(因此在PowerPC上移动32位可以格式化您的硬盘驱动器,它是不是保证产生零)。的成本消除这种未定义的行为是编译器必须要做的为变量移位触发一个额外的操作(如'and'),其中将使它们在普通cpu上的成本增加一倍。