为什么左变量与变量产生不同的结果与常量?
编译完下面的代码后,我得到了一个奇怪的结果,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.
在第二种情况下,编译器会自己计算这个值,这会变成溢出,并给出零。
如果您更改编译器选项以优化代码,则很可能(但不是确定的),如果更改编译器选项以优化代码,则两种情况都会给出零。如果你要做一个较小的班次循环,你会得到你想要的行为(尽管你可能会发现“相互影响”的行为,因为这个数字变成了负数,所以最好对所有班次操作使用无符号数) 。
因为您正在调用未定义的行为。移动比现有类型更多的位并没有被定义为具有任何特定的行为。
参见http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1
超大位移量:由32点或更多的位移一个uint32_t的是 未定义。我的猜测是,这是因为在各种CPU上的底层 移位操作做了不同的事情:对于 例如,X86将32位移位量截断为5位(所以32位移位与移位相同由0位),但PowerPC将32位移位量截断为6位(所以移位32产生零)。 由于这些硬件差异,行为完全是由C定义的 (因此PowerPC上的32位移位可能会格式化您的 硬盘驱动器,它是而不是保证产生零)。消除这种未定义行为的成本 是编译器必须为 发出额外的操作(如'和')以进行变量转换,这会使其在公共CPU上花费两倍。
开发代码时请始终启用并阅读警告。例如Gcc告诉你“左移count> = type的宽度”。使用clang的'-fsanitize = ...'选项也是一个好主意。 – 2013-05-13 07:23:28
你发布的代码显然是C++,但你也用C标签?这两种语言往往偏离这样的边界情况,所以最好提问你真正感兴趣的语言。一般来说,对签名类型进行移位操作不是一个好主意。 – 2013-05-13 08:17:17