2

我在一篇关于未定义 C++ 行为的文章的 Shift Operators 部分中读到的内容让我感到困惑。

在 ARM 体系结构上,移位运算符总是表现得好像它们发生在 256 位模式空间中,而与操作数大小无关——也就是说,模式仅每 256 个位置重复或“回绕”一次。另一种思考方式是模式以 256 为模移动指定数量的位置。然后,当然,结果只包含模式空间的最低有效位。

这些表格特别奇怪:

Given a 32-bit integer with a value of 1:
+-----------------------------------+
| Shift left    ARM    x86    x64   |
+-----------------------------------+
| 32            0      1      1     |
| 48            0      32768  32768 |
| 64            0      1      1     |
+-----------------------------------+

这些价值观是什么,它们为什么重要?

移位运算符不换行。根据 C++ 规范,如果将 32 位值左移 32,结果始终为 0。 (编辑:我错了,请参阅答案!)那么这篇文章的目的是什么?什么是未定义的行为?

当我在 x86 上运行此代码时,我得到0

printf("%d", 1 << 32);

假设这个代码片段说明了这个问题:

// C4293.cpp
// compile with: /c /W1
unsigned __int64 combine (unsigned lo, unsigned hi) {

    return (hi << 32) | lo;   // C4293

    // try the following line instead
    // return ( (unsigned __int64)hi << 32) | lo;
}

我希望返回值是lo,因为程序员移走了所有hi位。警告很好,因为这可能是一个错误,但我没有看到任何未定义的行为......

4

3 回答 3

4

如果您使用 x86 或 x64 机器指令来移位值,它们将屏蔽移位量并且仅使用低位进行实际移位。其他一些硬件可能不会这样做。

这就是为什么它是未定义的。

在您使用文字的示例中1 << 32,编译器可能会计算该值,这就是它的原因0。在真正的 x86 硬件上尝试操作,你会得到1.

于 2012-10-26T13:32:15.637 回答
3

根据 C++ 规范,如果将 32 位值左移 32,则结果始终为 0

不,这不是标准所说的。将 32 位类型移动 32 位或更多 (5.8/1) 是未定义的行为

由于在 ARM(没有 257 位或更高类型)上移动 256 位是未定义的行为,因此 CPU 完全有权在该点进行换行。

于 2012-10-26T13:33:49.093 回答
3

结果的类型是提升的左操作数的类型。如果右操作数为负数,或者大于或等于提升的左操作数的位长度,则行为未定义。

这是来自 C++11 的 §5.8/1。如果您的整数是 32 位的,则不能移动 32 位(右移或左移,无论左操作数的符号性如何)。

于 2012-10-26T13:26:30.010 回答