为什么左移与变量产生不同的结果与常数?

JDe*_*ein 5 c++ bit-shift

在编译下面的代码后,我得到了一个奇怪的结果,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;
}
Run Code Online (Sandbox Code Playgroud)

Sti*_*sis 5

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

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

超大移位量:未定义将uint32_t移位32位或更多位.我的猜测是,这是因为各种CPU上的基础移位操作对此做了不同的事情:例如,X86将32位移位量截断为5位(因此移位32位与移位0相同比特),但PowerPC将32位移位量截断为6位(因此移位32会产生零).由于这些硬件差异,C的行为完全未定义(因此在PowerPC上移位32位可能会格式化您的硬盘,因此无法保证产生零).消除这种未定义行为的代价是编译器必须为变量移位发出额外的操作(如'和'),这会使它们在普通CPU上的成本增加一倍.


Mat*_*son 5

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

在第一种情况[至少在没有优化],编译器产生的指令来计算1 << 32-这在x86变成1 << (32 & 31)这是一样的1 << 0-因而你得到1.

在第二种情况下,编译器将计算值本身,该值变为溢出,并给出零.

很可能(但不确定)如果您更改编译器选项以优化代码,则两种情况都会为零.如果你要做一个较小班次的循环,你会得到你想要的行为(虽然你可能会发现"有趣"的行为,因为数字变为负数,所以最好使用unsigned进行所有的班次操作,真的) .