编译下面的代码后,我得到了一个奇怪的结果,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;
}
因为您正在调用未定义的行为。移动比类型中存在的更多位未定义为具有任何特定行为。
见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 上的成本增加一倍。
该标准没有定义,或者更确切地说,它将其定义为“未定义的行为”,即在左移超出整数类型大小的情况下会发生什么。[这种未定义行为的原因是不同硬件的行为可能相同,也可能不同,例如向左移动 32 位]。
在第一种情况下[至少没有优化],编译器生成指令来计算1 << 32
——在 x86 上变成1 << (32 & 31)
哪个是相同的1 << 0
——因此你得到 1。
在第二种情况下,编译器将自己计算值,这会变成溢出,并给出零。
如果您更改编译器选项以优化代码,很有可能(但不确定)这两种情况都为零。如果您要进行较小班次的循环,您将获得您想要的行为(尽管您可能会发现数字变为负数的“有趣”行为,因此最好对所有班次操作使用无符号,真的) .