位运算,顾名思义,就是对字节中的每个位进行一位一位的运算。
常见的运算符有:位与(&)、位或(|)、位非(~)、异或(^)、左移(<<)、右移(>>)。
基本规则:
1.位与运算(&):有0为0,双1为1
a | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
b | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
a & b | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
2.位或运算(|):有1为1,双0为0
a | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
b | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
a | b | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
3.位非运算(~):也叫按位取反运算,1变0,0变1
a | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
~a | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
4.异或运算(^):相同为0,不同为1
a | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
b | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
a ^ b | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
5.左移(<<):所有位依次向左移动,右边补0
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
1 << 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
6.右移(>>):所有位依次向右移动,左边补0
128 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
128 >> 5 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
位运算在嵌入式编程中的应用
对数值进行位运算有很多实际应用,比如:
左移(5<<3)就相当于5乘2的3次方,也就是:5*(2^3)=40
右移(8>>2)就相当于8除2的2次方,也就是:8/(2^2)=2
还有很多巧妙的用法,不过单纯对数值进行位运算,可读性不强,用适当的代码或函数也能完成同样的操作。
最常见的应用是在嵌入式编程中,对字节的某个位或几个位进行控制。
注意:
1.在进行位操作时,我们其实根本不用关心这个变量的数值到底是多少,只关注我们希望的某个位或者某几个位到底是0还是1。
2.在位操作中,一般不要直接用(=)赋值(除非赋初值),因为这样赋值会把所有的位全都改变了,而我们实际上只想改变某些位,对其他的位并不想改变。所以应该用(&=、|=、^=)来赋值,只改变需要改变的位,其他位不变。
(&=):因为任何数&0为0,一般用来把某些位清零
(|=):因为任何数|1为1,一般用来把某些位设为1
(^=):用来翻转某个位,1^1=0,也就是把1翻转为0;0^1=1,也就是把0翻转为1
以下是几个常见的位运算的例子:
uint16_t a = 0x1234; //注意这是16进制数,每个数字占4个位
a &= 0xFF0F; //把第4-7位清零,其他位不变
a |= 0x0070;//16进制7是二进制0111,把4-7位设置成0111,其他位不变
a |= 1<<2;//(1<<2)是二进制100,也就是把第2位设为1,其他位不变
a ^= 0x0004;//16进制4是二进制100,也就是单独翻转a的第2位,其他位都不变
a = ~0x0000;//对每一位全部取反,现在a的每个位都变成1了,也就是0xFFFF
以上是个人理解,说的不对的地方见谅,请多批评指正。