为什么先左移然后右移,而不是 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
。据我所知,它执行以下操作:
b << 16
会给出 0x33440000
>> 16
会给出 0x00003344
因此,最高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 位的复制值填充。
我遇到了这段 C 代码:
typedef int gint
// ...
gint a, b;
// ...
a = (b << 16) >> 16;
为了便于记号,我们假设此时 b = 0x11223344
。据我所知,它执行以下操作:
b << 16
会给出0x33440000
>> 16
会给出0x00003344
因此,最高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 位的复制值填充。