一、介绍
关于移位操作符,我们分为两类,一个是左移操作符<< , 一个是右移操作符>>
且两位操作符都与二进制的补码息息相关。
补码详细请看http://t.csdn.cn/9W84t
警告:对于移位运算符,不要移动负数位,这个是标准未定义的。
二、<< 左移操作符
左移操作符: 左边丢弃,右边补0
例如:
int a = 7
// 0000 0000 0000 0000 0000 0000 0000 0111 - 7的原码,也是7的补码
a = a << 1
//将7的补码向左移动一位,随后在右边补上一个数字0
//因为int的内存之中只能存放32位bit,所以从左往右数32位数留下,多出的数字剔除
//而在此运算中,最左边移动的哪一位数字,就是被剔除的
向左移动的一位
↓
// 0 0000 0000 0000 0000 0000 0000 0000 1110
↑
向右补上的数字0
// 0000 0000 0000 0000 0000 0000 0000 1110
//因为是正数,补码原码是一致的,所以可以直接进行计算,最后得出的结果是14
当然,以上是针对与正数的操作,而对于负数也是同样的道理,不过负数要先变成补码的形式,随后在进行移位操作。
int b = -7
// 1000 0000 0000 0000 0000 0000 0000 0111 - -7的原码
// 1111 1111 1111 1111 1111 1111 1111 1000 - -7的反码
// 1111 1111 1111 1111 1111 1111 1111 1001 - -7的补码
b = b << 1
//进行左边移动一位,右边补上一位数字0
左边移动一位
↓
// 1111 1111 1111 1111 1111 1111 1111 1001
↑
右边需要补上一位数字0
左边移动一位
↓
// 1 1111 1111 1111 1111 1111 1111 1111 0010
↑
右边补上一位数字0
//整理得到:
// 1111 1111 1111 1111 1111 1111 1111 0010
最后得到结果,需要变回原码
// 1000 0000 0000 0000 0000 0000 0000 1101 - -7 << 1 的反码
// 1000 0000 0000 0000 0000 0000 0000 1110 - -7 << 1 的原码
最后经过计算得知 -7<<1 的结果是 -14
通过以上结果,我们得知左移操作符<<具有乘2的效果。
7 << 1 = 14
7 << 2 = 28
7 << 3 = 56
7 << 4 = 112
-7 << 1 = -14
-7 << 2 = -28
-7 << 3 = -56
-7 << 4 = -112
从而得出结论:
a<<1 a×2
a<<2 a×2×2
a<<3 a×2×2×2
三、右移操作符 >>
右移操作符分为逻辑右移和算术右移。
1、逻辑右移
逻辑右移:左边用0填充,右边丢弃
例如:
int a = -1
// 1111 1111 1111 1111 1111 1111 1111 1111 - -1的补码
a >> 1
左边补一个数字0
↓
// 01111 1111 1111 1111 1111 1111 1111 1111
↑
右边丢弃一位
左边补一个数字0
↓
// 0111 1111 1111 1111 1111 1111 1111 1111 - -1 >> 1 的结果
↑
右边丢弃一位
逻辑右移有一个弊端,如上所示,若是一个负数需要进行右移时,使用了逻辑右移,那么这个负数会变成正数。
也因此,绝大多数的编译器并不采用逻辑右移,反而采用算术右移。
2、算术右移
算术右移:右边丢弃,若是负数左边补充1,若是正数左边补充0
例如:
int b = -1
// 1111 1111 1111 1111 1111 1111 1111 1111 - -1的补码
b >> 1
左边补一个数字1
↓
// 11111 1111 1111 1111 1111 1111 1111 1111
↑
右边丢弃一位
左边补一个数字1
↓
// 1111 1111 1111 1111 1111 1111 1111 1111 - -1 >> 1 的结果
↑
右边丢弃一位
三、注意事项
移位操作符的操作数只能是整数。
float f = 4.5f