2013-05-13 123 views
5

编译完下面的代码后,我得到了一个奇怪的结果,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; 
} 
+0

开发代码时请始终启用并阅读警告。例如Gcc告诉你“左移count> = type的宽度”。使用clang的'-fsanitize = ...'选项也是一个好主意。 – 2013-05-13 07:23:28

+0

你发布的代码显然是C++,但你也用C标签?这两种语言往往偏离这样的边界情况,所以最好提问你真正感兴趣的语言。一般来说,对签名类型进行移位操作不是一个好主意。 – 2013-05-13 08:17:17

回答

5

标准没有定义,或者说,它把它定义为“未定义行为”,什么超出了整数类型的大小的情况下左移发生。 [这种未定义行为的原因是不同的硬件可能表现不一样,例如,向左移动32位]。

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

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

如果您更改编译器选项以优化代码,则很可能(但不是确定的),如果更改编译器选项以优化代码,则两种情况都会给出零。如果你要做一个较小的班次循环,你会得到你想要的行为(尽管你可能会发现“相互影响”的行为,因为这个数字变成了负数,所以最好对所有班次操作使用无符号数) 。

5

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

参见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上花费两倍。