6

这有点让我发疯。

int a = 0xffffffff;
int b = 32;
cout << (a << b) << "\n";
cout << (0xffffffff << 32) << "\n";

我的输出是

-1
0

为什么我没有得到

0
0
4

3 回答 3

10

简短的回答是,由于您使用的是 32-bit 的实现int,因此语言标准说 32-bit shift 是未定义的行为。C 标准(第 6.5.7 节)和 C++ 标准(第 5.8 节)都说

如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

但如果你想知道为什么

许多计算机都有可以移位寄存器中的值的指令,但硬件只处理实际需要的移位值。例如,当移位一个 32 位字时,只需要 5 位来表示 0 ... 31 的移位值,因此硬件可能会忽略高阶位,并且在 *86 机器上会这样做(8086 除外)。为了使编译器实现可以只使用指令而不生成额外的代码来检查移位值是否太大,C 标准的作者(其中许多代表编译器供应商)裁定较大量移位的结果是未定义的。

您的第一次移位是在运行时执行的,它遇到了这种情况……您的机器只考虑 b 的低 5 位,它们为 0,因此不会发生移位。您的第二次移位是在编译时完成的,编译器会以不同的方式计算该值,并且实际上会执行 32 位移位。

如果你想移动的量可能大于你移动的东西的位数,你需要自己检查值的范围。一种可能的方法是

#define LEFT_SHIFT(a, b) ((b) >= CHAR_BIT * sizeof(a)? 0 : (a) << (b))
于 2013-04-18T02:16:31.580 回答
10

当您将一个值移位不少于其大小的位数(例如,对于 32 位整数,32 位或更多位)时,会发生未定义的行为。您刚刚遇到了这种未定义行为的示例。

于 2013-04-18T02:05:22.117 回答
0

C++ 标准说 :: 如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。由于 GCC 无法处理负数或超出可预测的类型宽度或陷阱的数量的移位;它们始终被视为未定义。所以行为没有定义。

于 2013-04-18T04:12:03.167 回答