C 语言还提供了一组移位运算,向左或者向右移动位模式。对于一个位表示为 []的操作数 x,C 表达式 x<<k 会生成一个值,其位表示为 [] 。也就是说, x 向左移动 k 位,丢弃最高的 k 位,并在右端补 k 个 0。移位量应该是一个 0~w-1 之间的值。移位运算是从左至右可结合的,所以 x<<j<<k 等价于 (x<<j)<<k。
有一个相应的右移运算 x>>k,但是它的行为有点微妙 。一般而言,机器支持两种形式的右移:逻辑右移和算术右移。逻辑右移在左端补 k 个 0,得到的结果是 [] 。算术右移是在左端补 k 个最高有效位的值,得到的结果是[] 。这种做法看上去可能有点奇特,但是我们会发现它对有符号整数数据的运算非常有用。
让我们来看一个例子,下面的表给出了对一个 8 位参数 x 的两个不同的值做不同的移位操作得到的结果:
操作 | 值 |
---|---|
参数 x | [0110 0011] [1001 0101] |
x << 4 | [0011 0000] [0101 0000] |
x >>> 4 (逻辑右移) | [0000 0110] [0000 1001] |
x >> 4 (算术右移) | [0000 0110] [1111 1001] |
斜体的数字表示的是最右端(左移)或最左端(右移)填充的值。可以看到除了一个条目之外,其他的都包含填充 0,唯一的例外是算术右移 [1001 0101] 的情况。因为操作数的最高位是 1,填充的值就是 1。
C 语言标准并没有明确定义对于有符号数应该使用哪种类型的右移——算术右移或者逻辑右移都可以 不幸地,这就意味着任何假设一种或者另一种右移形式的代码都可能会遇到可移植性问题。然而,实际上,几乎所有的编译器 / 机器组合都对有符号数使用算术右移,且许多程序员也都假设机器会使用这种右移。另一方面,对于无符号数,右移必须是逻辑的。
与 C 相比, Java 对于如何进行右移有明确的定义。表达是 x>>k 会将 x 算术右移 k 个位置,而 x>>>k 会对 做逻辑右移。
网友评论