C++ 中右移的未定义行为
Undefined behavior of right-shift in C++
来自cppreference.com:
For unsigned a and for signed a with nonnegative values, the value of
a >> b is the integer part of a/2b . For negative a, the value of a >>
b is implementation-defined (in most implementations, this performs
arithmetic right shift, so that the result remains negative).
In any case, if the value of the right operand is negative or is
greater or equal to the number of bits in the promoted left operand,
the behavior is undefined.
如果右操作数大于或等于提升的左操作数中的位数,为什么我们会有未定义的行为?
在我看来,结果应该是 0(至少对于 unsigned/positive 整数)...
特别是 g++(版本 4.8.4,Ubuntu):
unsigned int x = 1;
cout << (x >> 16 >> 16) << " " << (x >> 32) << endl;
给出:0 1
C++ 的目标之一是允许快速、高效的代码,"close to the hardware"。在大多数硬件上,整数右移或左移可以通过单个操作码实现。问题是,在这种情况下,不同的 CPU 具有不同的行为,其中移位幅度大于位数。
因此,如果 C++ 强制执行移位操作的特定行为,则在为其操作码行为不符合所有标准要求的 CPU 生成代码时,编译器将需要插入检查和逻辑以确保在所有情况下,结果均由标准定义。几乎所有内置移位运算符的使用都需要发生这种情况,除非优化器可以证明极端情况实际上不会发生。添加的检查和逻辑可能会减慢程序速度。
举个具体的例子,x86 将移位计数修整为 5 位(64 位移位为 6 位),而 ARM 将移位计数修整为 8 位。使用当前的 C++ 标准,CPU 的编译器可以使用单个操作码实现移位。
如果 C++ 标准以特定方式定义超过操作数长度的移位结果,则编译器至少针对 CPU 系列中的一个(如果结果需要C++ 不匹配硬件实现,就像你建议的行为一样)必须使用分支实现每个移位操作,这将产生所需的结果,而 CPU 操作码不会。
来自cppreference.com:
For unsigned a and for signed a with nonnegative values, the value of a >> b is the integer part of a/2b . For negative a, the value of a >> b is implementation-defined (in most implementations, this performs arithmetic right shift, so that the result remains negative).
In any case, if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined.
如果右操作数大于或等于提升的左操作数中的位数,为什么我们会有未定义的行为?
在我看来,结果应该是 0(至少对于 unsigned/positive 整数)...
特别是 g++(版本 4.8.4,Ubuntu):
unsigned int x = 1;
cout << (x >> 16 >> 16) << " " << (x >> 32) << endl;
给出:0 1
C++ 的目标之一是允许快速、高效的代码,"close to the hardware"。在大多数硬件上,整数右移或左移可以通过单个操作码实现。问题是,在这种情况下,不同的 CPU 具有不同的行为,其中移位幅度大于位数。
因此,如果 C++ 强制执行移位操作的特定行为,则在为其操作码行为不符合所有标准要求的 CPU 生成代码时,编译器将需要插入检查和逻辑以确保在所有情况下,结果均由标准定义。几乎所有内置移位运算符的使用都需要发生这种情况,除非优化器可以证明极端情况实际上不会发生。添加的检查和逻辑可能会减慢程序速度。
举个具体的例子,x86 将移位计数修整为 5 位(64 位移位为 6 位),而 ARM 将移位计数修整为 8 位。使用当前的 C++ 标准,CPU 的编译器可以使用单个操作码实现移位。
如果 C++ 标准以特定方式定义超过操作数长度的移位结果,则编译器至少针对 CPU 系列中的一个(如果结果需要C++ 不匹配硬件实现,就像你建议的行为一样)必须使用分支实现每个移位操作,这将产生所需的结果,而 CPU 操作码不会。