本节书摘来自异步社区《嵌入式Linux与物联网软件开发——C语言内核深度解析》一书中的第2章,第2.2节,作者朱有鹏 , 张先凤,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.2 常用位操作符
2.2.1 位与(&)
位与就是对数的二进制位进行运算。两个数每个二进制位的运算规则按照如下规则运算。该规则就是其真值表。
& 0 1
0 0 0
1 0 1
从其运算规则(真值表)可以看出,只有1和1进行与运算的结果是1,其余的全是0。如果我们将1当做真,0当做假的话,按照与运算的要求,两个为真才为真,只要有一个为假就为假。好了,我们看下面一个例子。
3 & 5 = ?
分析可知这两个是十进制数,所以先把这两个数都转化为二进制数。
3转化二进制:0b0011
5转化二进制:0b0101
将这两个数的二进制形式按照上面的运算规则进行按位与运算。
0b0011 (3)
& 0b0101 (5)
= 0b0001 (1)
将得到的二进制结果0b0001变为十进制,十进制结果为1。
所以可以得出结论:`
javascript
3 & 5 = 1。
扩展:&(按位与)和&&(逻辑与)的区别
&&(逻辑与)是将要运算的两个数看做一个整体,而这个整体如果是0,则该数被定义成逻辑假(0);如果该数不为0(不管是正的还是负的),则被定义成逻辑真(1)。来看几个小例子。
3 && 5 = ?
分析:3(逻辑真),5(逻辑真),真 && 真 = 真,所以结果为真,即```javascript
3 &`
& 5 = 1。
3 && 0 = ?
分析:3(逻辑真),0(逻辑假),真 && 假 = 假,所以结果为假,即`
javascript
3 && 0 = 0。
3 && -5 = ?
分析:3(逻辑真), -5(逻辑真),真 && 真 = 真,所以结果为真,即```javascript
3
&& -5 = 1。
2.2.2 位或(|)
对两个数的二进制位进行或运算,其真值表如下。
| 0 1
0 0 1
1 1 1
从其真值表可以看出,只有0和0进行或运算的结果是0,其余的全是1。对于位或运算来说,运算的两个位,只要有一个为1结果就为1,否则都为0。如下面这个例子。
3 | 5 = ?
将十进制转化为二进制。
3转化为二进制:0b0011
5转化为二进制:0b0101
对这两个数的二进制形式按照上面的运算规则进行按位或运算。
0b0011 (3)
| 0b0101 (5)
= 0b0111 (7)
将二进制结果0b0111转化为十进制,十进制结果为7。
所以可以得出结论:3 | 5 = 7。
扩展:|(按位或) 和 ||(逻辑或)的区别
||(逻辑或)是将要运算的两个数都看成一个整体,而这个整体如果是0,则该数被定义成逻辑假(0);如果该数不为0(不管是正的还是负的),则被定义成逻辑真(1)。来看下面这个小例子。
3 || 5 = ?
分析:3(逻辑真),5(逻辑真),真 || 真 = 真,所以结果为真,即3 || 5 = 1。
0 || 0 = ?
分析:0(逻辑假),0(逻辑假),假 || 假 = 假,所以结果为假,即0 || 0 = 0。
3 || -5 = ?
分析:3(逻辑真) -5(逻辑真),真 || 真 = 真,所以结果为真,即3 || -5 = 1。
2.2.3 位取反(~)
位取反就是将操作数的二进制位逐个按位取反(1变成0,0变成1),其真值表如下。
~0 = 1
~1 = 0
从上真值表中不难发现规律,取反后,1变0,0变1,比如下面这个例子。
~10 = ?
将十进制转化为二进制。
10转化为二进制:0b1010
对操作数的二进制形式按位取反。此处为了方便说明,暂时不考虑更高位补齐。实际编程位取反时要考虑取反的数的数据类型,然后在高位补足0,这时0会在取反时变为1。
~ 0b1010 (10)
0b0101 (5)
将二进制结果0b0101转化为十进制,结果为5。
所以可以得出结论:~10 = 5
扩展:~(按位取反) 和 !(非)的区别
!(非)是将操作数看成一个整体,而这个整体如果是0,则该数被定义成逻辑假(0);如果该数不为0(不管是正的还是负的),则被定义成逻辑真(1)。来看几个小例子。
!10 = ?
分析:10(逻辑真) 非真就是假,所以结果为假,即!10 = 0。
!0 = ?
分析:0(逻辑假) 非假就是真,所以结果为真,即!0 = 1。
!(-10) = ?
分析:-10(逻辑真) 非真就是假,所以结果为假,即!(-10) = 0。
2.2.4 位异或(^)
位异或就是将两个数的二进制位进行位异或运算。位运算的真值表如下。
^ 0 1
0 0 1
1 1 0
从其运算规则(真值表)可以看出,两个位如果相等,结果为0,不等则结果为1。比如下面的例子。
3 ^ 5 = ?
将十进制转化为二进制。
3转化为二进制:0b0011
5转化为二进制:0b0101
将这两个数的二进制形式按照上面的运算规则进行按位与运算。
0b0011 (3)
^ 0b0101 (5)
= 0b0110 (6)
将二进制结果0b0110转为十进制,十进制结果为6。
0b0110转化为十进制:6
所以可以得出结论:3 ^ 5 = 6。
2.2.5 左移位(<<)
左移位就是将一个操作数的各二进制位全部左移若干位,左边移出去的二进制位丢弃,右边空出的二进制位补0。话不多说,来看个例子。
5 << 2 = ?
将十进制化为二进制。
5转化为二进制:0b00000101
对操作数0b00000101开始进行左移位两次。
0b00000101 (5)
第一次左移位 0b00001010 (10) = 5 * 2
第二次左移位 0b00010100 (20) = 10 * 2
将二进制结果0b00010100转为十进制,十进制结果为20。
所以可以得出结论:5 << 2 = 20。在这个移位的过程中,我们也发现了一个规律,每进行一次左移位操作,得到的结果是原操作数的一倍(x << n = x * 2^n)。
2.2.6 右移位(>>)
右移位就是将一个操作数的各二进制位全部右移若干位,左边的二进制位补0或者补1(如果操作数是无符号数或有符号正数就补0,如果是有符号负数就补1),右边的二进制位丢弃。话不多说,来看例子。
5 >> 2 = ? (-5) >> 2 = ?
将十进制转化为二进制。
5转化为二进制:0b00000101
-5转化为二进制:0b11111011
对操作数0b00000101开始进行右移位两次。
0b00000101 (5) 0b11111011 (-5)
第一次右移位:0b00000010 (2) = 5 / 2 0b11111101 (-3)
第二次右移位:0b00000001 (1) = 2 / 1 0b11111110 (0)
将得出的结果(二进制)还原成十进制形式。
0b00000001转化为十进制:1
0b11111110转化为十进制:0 (按照负数解析)
所以可以得出结论:5 >> 2 = 1。在这个移位的过程中,我们也同样发现了一个规律,每进行一次右移位操作,得到的结果是原操作数的一半(x >> n = x / 2^n)。