【问题标题】:why does left shift with variables generate different result from that with constant?为什么带变量的左移与带常量的左移产生不同的结果?
【发布时间】:2013-05-13 05:44:46
【问题描述】:

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

【问题讨论】:

  • 在开发代码时请始终启用并阅读警告。例如,Gcc 会告诉您“左移计数 >= 类型宽度”。使用 clang 的 -fsanitize=... 选项也是一个好主意。
  • 你发布的代码明明是C++,但你也用C标记?这两种语言在这样的边界情况下往往会出现偏差,因此最好针对您真正感兴趣的语言提出问题。通常,对有符号类型进行移位操作并不是一个好主意。

标签: c++ bit-shift


【解决方案1】:

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

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

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

很有可能(但不确定)如果您更改编译器选项以优化代码,则两种情况都为零。如果您要进行较小班次的循环,您将获得您想要的行为(尽管您可能会发现数字变为负数的“有趣”行为,因此最好对所有班次操作使用无符号,真的) .

【讨论】:

    【解决方案2】:

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

    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 上的成本增加一倍。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-23
      • 2022-07-19
      • 1970-01-01
      • 2017-10-30
      • 1970-01-01
      • 2021-03-03
      • 2012-05-12
      • 1970-01-01
      相关资源
      最近更新 更多