13.2.1 按位运算
按位运算,即把整数当做二进制进行运算。
·& | 按位的与 | |
·\ | 按位的或 | |
·~ | 按位取反 | |
·<< | 左移 | |
·>> | 右移 |
按位与 &
如果(x)_i==1且(y)_i==1
,那么(x&y)_i=1(第i位)否则=0
如:
0101 1010 5A
1000 1100 8C 做运算后:
0000 1000 08
按位与的应用:
- 让某一位或某些位=0,如x & 0xFE(1111 1110) 会让最后一位变为0
- 取某个数中的某一段:如x & 0xFF (在32位int中是 0000 0000 0000 0000 0000 0000 1111 1111)这样只会留下最后8位
按位或
应用:
- 使得某一位/某几位变为1: x | 0x01
- 把两个数拼起来: 0x00FF | 0xFF00
按位取反:0变为1,1变为0
和补码不太一样,补码是用1 0000 0000-该数。可以试下(char)~c和(char)-c的值。
逻辑运算,相当于把所有非0值变为1后做按位运算。
因此还是有不同的,如果忘记逻辑运算是两个&|的话就会出问题。
按位异或^,两位相等结果为0,两位不等结果为1.
对同一个值做两次异或,还是原来的值。可以拿来做加密的编码,也可以判断两段码是否相等。
13.2.2 移位运算
i<<j:i中所有位向左移动j个位置,右边填入0
所有小于int的类型,移位以int的方式做,结果是int。
往左移1位就等价于乘了2.最多移动多少位取决于int的大小。x<<1
右移相当于/2。
对于小于int的类型,移位以int的方式做,且结果是int;
对于unsigned类型,左边填0
对于signed类型,左边填入原来的最高位(判断符号的01),保持符号不变。而往左移动的时候是不管符号位的。
移位的位数不要用负数,这是没有定义的行为!
13.2.3 位运算例子
有什么用处?
例1:输出一个数字的二进制
unsigned后面没有跟类型,则是默认为unsigned int。
mask依次是
1000 0000
0100 0000
0010 0000
...
这样每一位依次取余,看每一位。
另外一件事:做单片机时常遇到这样的特殊功能寄存器(SFR)
怎么把对应比特置为0或1?
第几位就是1u<<几
如SBS是1,左移2位是100
PE是1,左移3位是1000
100
1000
1100(或后得到的结果)
用或使某些比特为1,用和使某些比特为0。
13.2.4 位段
在SFR表中可知,有的也不止一个比特。但之前的技巧只能控制一个比特。
如何控制多个比特?位段,把一个int的若干位组合成一个结构。
冒号后面的数字表示 该成员占几个比特。
prtBin函数就是刚刚看过的输出二进制位。
输出:sizeof(uu)=4
二进制输出后五位10010
当我们把uu.trailing=0注释掉后,sizeof(uu)没变,但是二进制位输出变了。后五位仍然是10010,前面没有赋初值(没有让trailing=0),是乱七八糟的0和1.
当我们int trailing=32,仍然让uu.trailing=0,这时sizeof(uu)=8(所有位数加起来超过了32,所以用两个int来表达)二进制输出仍然只有后五位10010.
位段可以直接用位段的成员名称来访问,比移位、与、或还要方便。
编译器会安排其中的位的排列,不具有可移植性(比如老师的例子是从最右边排起,可能自己试验时会从最左边排。)所需要的位超过一个int时会安排多个int。
总而言之,位段是操作和硬件相关的底层操作。