位移运算
1.左移
//C左移表达式
x<<k;
对于一个n位的操作数x,x<<k操作会生成一个值:x向左移动k位,丢弃最高的k位,并在右端补k个0。位移量应该是一个0~n-1之间的值。
位移运算是从左至右可结合的,所以x<<j<<k等价于(x<<j)<<k。
2.右移
//C右移表达式
x>>k;
一般而言,机器支持两种形式的右移:逻辑右移和算术右移。逻辑右移在左端补k个0;算术右移是在左端补k个最高有效位。
3.一个例子
Tables | Cool |
---|---|
参数x | [01100011] [10010101] |
x << 4 | [00110000] [01010000] |
x >> 4 (逻辑右移) | [00000110] [00001001] |
x >> 4 (算术右移) | [11110110] [11111001] |
C语言并没有明确定义应该使用哪种类型的右移。
- 对于无符号数据(unsigned声明的整形对象),右移必须是逻辑的;
- 对于有符号数据(默认的声明的整型对象),算术的或者逻辑的右移都可以。然而实际上,几乎所有的编译器/机器组合都对有符号数据使用算术右移,且许多程序员也都假设机器会使用这种右移。
另一方面,Java对于如何进行右移有明确的定义。表达式x>>k会将x算术右移k个位置,而x>>>k会对x做逻辑右移。
位移操作的注意事项
1.位移k位,k很大
问题:对于一个由w位组成的数据类型,如果要移动k(k>>w)位会得到什么结果呢?
例如在一个32位的机器上计算下面的表达式会得到什么结果:
int lval = 0xFEDCBA98 << 32;
int aval = 0xFEDCBA98 >> 46;
unsigned uval = 0xFEDCBA98 << 40;
C 语言标准很小心地规避了说明在这种情况下该如何做。在许多机器上,当移动一个w位的值,移位指令只考虑位移量的低 log2w 位,因此实际上位移量就是通过计算k mod w得到的。在一台32位的机器上,上面三个位移运算分别是移动0、4和8位,得到结果:
lval 0xFEDCBA98
aval 0xFFEDCBA9
uval 0x00FEDCBA
不过这种行为对于C程序来说是没有保障的,所以位移数量应该保持小于字长。
2.与位移有关的操作符优先级问题
1<<2+3<<4
在C语言中,加法(和减法)的优先级比位移运算要高,然后按照从左至右结合性规则,上述表达式应该这样解释:
(1<<(2+3))<<4
参考资料
《深入理解计算机系统》 第2章 信息的表示和处理