为什么x+0和x0有不同的结果?
为什么 x+0 和 x|0 有不同的结果?
下面是我的代码。
我的环境是 WSL (Debian Sid) + GCC 10.2.1。
#include <stdio.h>
/**
* Do rotating left shift. Assume 0 <= n < w
* Examples when x = 0x12345678 and w = 32:
* n = 4 -> 0x23456781, n = 20 -> 0x67812345
*/
unsigned rotate_left(const unsigned x, const int n)
{
const int w = sizeof(unsigned) << 3;
return (x << n) + (x >> (w - n));
}
int main()
{
int x = 0x12345678;
printf("n = 4, %#x -> %#xn", x, rotate_left(x, 4));
printf("n = 20, %#x -> %#xn", x, rotate_left(x, 20));
printf("n = 0, %#x -> %#xn", x, rotate_left(x, 0));
}
当 n = 0 时,结果为 0x2468acf0。
当我用 替换return (x << n) + (x >> (w - n))时return (x << n) | (x >> (w - n)),我得到 0x12345678。
回答
如果x是最多 32 位的整数类型,则x>>32是Undefined Behavior,这意味着结果绝对可以是任何东西(并且在不同的程序中可以是不同的东西)。(也适用于x<<32。)[注 1]
从 C 标准的 §6.5.7 第 3 段中,关于<<和>>运算符,重点补充说:
如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。
因此,如果n <= 0或,您的旋转功能将不起作用n >= w,您应该测试这些情况而不是假设它们不会发生(因为它们显然会发生)。
笔记
- 实际上,在 Intel 硬件(可能还有其他硬件)上,操作数宽度的移位是空操作,而不是“清除为 0”。那么
x>>32是x,由于是x<<0,这样的总和的两倍x,而按位或正好x。但是你不能依赖这个事实,因为它是Undefined Behavior,编译器优化可能会导致其他任意结果。
- @内森;`sizeof(unsigned)` 可能是 4,在这种情况下 `sizeof(unsigned)<<3`,也就是 **代码实际所说的**,是 32。