这两天学习了C的位运算,记录一下,这个知识也是看Linux内核的基础。
与或非
位与& 和 逻辑与&&
- 位与的真值表的特点是:只有1&1结果才1;1&0,0&1,0&0结果都为0.
- 位与操作与逻辑与操作一起记住,把非0的数看成是1,0做为0,然后再进行与&运算,例如0xffff&&0x11=1;
位或| 和 逻辑或||
- 位或只有两个0位或结果为0,其余都是1,真值表:1|1=1,1|0=1,0|1=1,0|0=0
- 两个操作数作为整体操作,把非0的数看成是1,0看成是0,进行位与操作。
位取反~和逻辑取反!
- 位取反|每个二进制位按位取反,0变1,1变0。
- 逻辑取反,非0的数当做真,0作为假,逻辑运算:真变假,假变帧。
下面是例子,ARM中的移位操作都是无符号数;ARM中的读改写都是一体的,想要对一个寄存器中的某些特定位进行读改写,要将这个寄存器中所有的值读出来,然后修改特定位,最后再将修改好的值,写入寄存器。
与或非使用场景
特定位清零用&
如果希望将一个寄存器的某些特定位变为0,而不影响其他位,可以构造一个合适的由1和0组成的数和这个寄存器原来的值进行与操作&,可将特定位清零。
例如:将0xAAAAAAAA bit8-bit15清0,其他位不变,可以将这个数与oxFFFF00FF进行位与操作,得到0xAAAA00AA
特定位置1用|
或|运算的特定,与1则为1,遇到0为0.
特定位取反用异或操作
异或运算特点:相等为0,不等为1.记忆方法是:不同时进行或运算。
左移<<和右移>>
左移:移动后,后面的空缺自动补零。
右移:分为逻辑右移和算术右移
- 逻辑右移:不管什么类型,移动后,左边补零‘
- 算术右移,如果是无符号数,则左边补零;如果是负数,左边补1;
如何构造特定的数
使用位移<<和>>
想要操作寄存器中的某些特定位,要构造一个数,与寄存器中的数进行运算,怎样才能构造这样的数?可以使用右移来构造,例如需要一个bit3-bit7为0的数,则可以11111<<3,写成0x1f<<3.就可以得到。
更难一点的:bit3-bit7为1,同时bit23-bit25为1,其余位为0.
解法:(0x1f<<3)|(7<<23);
使用位取反~ 获得特定位为0的二进制数
例如:获取bit4-bit10为0的数,其他位为1的数,如何做?
- 解法1:利用上面的方法,就是把这个数的其他位都置1,这样操作:(0xf<<0)|(0xffff1<<11),找个数左移11位,就可以得到。
这种方法缺点是,1太多了。 - 解法2:构造一个这个数的相反数,再与原来的数进行&操作。~(0x7f<<4).
总结
- 如果想要的这个数,1的位数比较少,大部分为0, 则可以通过连续很多个1左移得到。
- 如果想要的数,0的位数表较多,大部分位1,可先构建取反数,然后再取反。
- 总之:要置1用|,要置0用&,取反用~,再配合左移和右移获得。
位运算练习
练习1 :给定一个整型数a,设置a的bit4,保证其他位不变。
a|(1<<3)
输出结果16.
练习2:给定一个整形数a,设置a的bit3~bit7,保持其他位不变。
a = a | (ob11111<<3)
a|= (0x1f << 3)
练习3:给定一个整型数a,清除a的bit15,保证其他位不变。
第一步: 1 << 15
第二步: ~(1 << 15)
第三步: a = a & (~(0x1ff))
最后a变为0
练习4:给定一个整形数a,清除a的bit15~bit23,保持其他位不变。
第一步:0x1ff<<15
第二步:~(0x1ff) << 15
第三步:a = a & ~(0x1ff) << 15
练习5:给定一个整形数a,取出a的bit3~bit8。
第一步:先将bit3-bit8不变,其余位全部清零。
a & (0x3f << 3)
第二步:再将这个数,右移3位。
a = a >> 3;
练习6:用C给一个寄存器的bit7~bit17赋值937(其余位不受影响)。
第一步:先将bit7-bit17清0
a = a & ~(0x7ff << 7);
第二步:再将937写入即可。
a = a | (937<<7)
练习7:用C语言将一个寄存器的bit7~bit17中的值加17(其余位不受影响)。
第一步:先读出bit7-bit17的值:将bit7-bit17不变,其余位都是0.
b = a ;
b = b & ~(0x7ff << 7)
b =b >> 7;
第二步: 再给这个值加上17,
b += 17;
第三步:将bit7-bit17清零
a = a & ~(0x7ff << 7)
第四步:
a |= (a <<b)
用宏完成上述操作(有空再整理)
用宏时,最起码的在#define后边加上括号,要不然就是最不专业的C程序员。
第一题,用宏定义将32位数x的第n位置位。
分析:置位的意思是,赋值为1.第n位对应的是bit(n-1)位。
第一步:(x | (1 << (n-1))) // 最外边加一层括号。
第二步:#define SET_BIT_N(x,n) (x | (1 << (n-1)))
专业的写法:(x | ((1U) << (n-1))) ,这种方式充分考虑了,1作为一个无符号整数
第二题,用宏定义将32位数x的第n位清零
第一步:x & ~(1U << (n-1))
第二步:#define CLEAR_BIT_N(x,n) (x & ~(1U) << (n-1))
还有更复杂的宏定义,留着看内核的时候,再过来看。