移位运算符的作用正如其名称所暗示的那样。他们移位。以下是对不同移位运算符的简要(或不那么简要)介绍。
运营商
-
>>
是算术(或有符号)右移运算符。
-
>>>
是逻辑(或无符号)右移运算符。
-
<<
是左移运算符,满足逻辑移位和算术移位的需要。
所有这些运算符都可以应用于整数值(int
, long
, 可能short
and byte
or char
)。在某些语言中,将移位运算符应用于小于的任何数据类型int
自动调整操作数的大小int
.
注意<<<
不是运算符,因为它是多余的。
另请注意C 和 C++ 不区分右移运算符。他们只提供>>
运算符,右移行为是为有符号类型定义的实现。答案的其余部分使用 C# / Java 运算符。
(在所有主流 C 和 C++ 实现中,包括 GCC 和 Clang/LLVM,>>
有符号类型上是算术。某些代码假设了这一点,但这不是标准所保证的。它不是不明确的, 尽管;该标准需要实现以一种或另一种方式定义它。但是,负符号数左移is未定义的行为(有符号整数溢出)。因此,除非您需要算术右移,否则通常最好使用无符号类型进行位移位。)
左移 (
整数作为一系列位存储在内存中。例如,数字 6 存储为 32 位int
将会:
00000000 00000000 00000000 00000110
将此位模式向左移动一位(6 << 1
) 将得到数字 12:
00000000 00000000 00000000 00001100
正如您所看到的,数字已向左移动了一位,右侧的最后一位数字用零填充。您可能还注意到,左移相当于乘以 2 的幂。所以6 << 1
相当于6 * 2
, and 6 << 3
相当于6 * 8
。一个好的优化编译器会尽可能用移位代替乘法。
非循环变速
请注意,这些是not循环移位。将该值向左移动一位(3,758,096,384 << 1
):
11100000 00000000 00000000 00000000
结果为 3,221,225,472:
11000000 00000000 00000000 00000000
“偏离末尾”的数字将丢失。它不会环绕。
逻辑右移 (>>>)
逻辑右移与左移相反。它们不是向左移动位,而是向右移动。例如,移动数字 12:
00000000 00000000 00000000 00001100
向右移动一个位置(12 >>> 1
) 将取回我们原来的 6:
00000000 00000000 00000000 00000110
所以我们看到向右移动相当于除以 2 的幂。
丢失的位消失了
然而,移位不能收回“丢失”的位。例如,如果我们改变这种模式:
00111000 00000000 00000000 00000110
向左 4 个位置(939,524,102 << 4
),我们得到 2,147,483,744:
10000000 00000000 00000000 01100000
然后向后移动((939,524,102 << 4) >>> 4
)我们得到 134,217,734:
00001000 00000000 00000000 00000110
一旦丢失了比特,我们就无法恢复原来的值。
算术右移 (>>)
算术右移与逻辑右移完全相同,只不过它不是用零填充,而是用最高有效位填充。这是因为最高有效位是sign位,或区分正数和负数的位。通过用最高有效位填充,算术右移可以保留符号。
例如,如果我们将此位模式解释为负数:
10000000 00000000 00000000 01100000
我们有数字-2,147,483,552。通过算术移位 (-2,147,483,552 >> 4) 将其向右移动 4 个位置将得到:
11111000 00000000 00000000 00000110
或数字-134,217,722。
所以我们看到,我们通过使用算术右移而不是逻辑右移保留了负数的符号。我们再一次看到我们正在执行 2 的幂除法。