为什么先左移然后右移,而不是 AND 运算?

Why first bitshifting left and then right, instead of AND-ing?

我遇到了这段 C 代码:

typedef int gint
// ...
gint a, b;
// ...
a = (b << 16) >> 16;

为了便于记号,我们假设此时 b = 0x11223344。据我所知,它执行以下操作:

因此,最高16位被丢弃。

如果 b & 0x0000ffff 也可以,为什么有人会写 (b << 16) >> 16?后一种形式不是更容易理解吗?在这种情况下是否有任何理由使用位移位?是否存在两者不能相同的极端情况?

对于无符号整数类型(例如,我们最初认为正在使用的uint32_t),

(b << 16) >> 16

等同于 b & (1<<16 - 1)

虽然对于带符号的整数类型,

(b << 16)

可能变为负数(即,低 int16_t 单独计算时会被视为负数),在这种情况下

(b << 16) >> 16
由于符号扩展,

将(可能)为负。在那种情况下,它与 & 掩码不同,因为最高位将被设置而不是零。


这种行为要么是故意的(在这种情况下,注释掉的 typedef 具有误导性),要么是一个错误。不看代码我就不知道了。

哦,我希望 gcc 在 x86 上的行为是双向的,但我无法评论它在 x86 之外的可移植性。正如 Lundin 指出的那样,左移可能是 UB,右移的符号扩展是实现定义的。

假设int的大小是32位,那么就不用移位了。事实上,带掩码的按位 & 将更具可读性、更便携和更安全。

应该注意负符号整数的左移会引发未定义的行为,而将东西左移到有符号整数的符号位也会引发未定义的行为。 C11 6.5.7(强调我的):

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.


(我能想到的唯一可能的理由是,对 16 位 CPU 进行一些过早的优化,编译器性能较差。如果将16 位块中的算术。但是在这样的系统上,int 很可能是 16 位,所以代码那时没有任何意义。)

附带说明一下,使用带符号的 int 类型也没有任何意义。此代码最正确和安全的类型应该是 uint32_t.

So, the 16 highest bits are discarded.

他们不是。尽管正式实现定义了如何对有符号类型执行右移操作,但大多数编译器这样做是为了复制符号位。

因此,作为该表达式的结果,最高 16 位由第 15 位的复制值填充。