目录
一、位操作符
1.位与&
2.位或|
3.位取反~
4.位异或^
5.位与,位或,位异或的特点总结
6.左移位《《 右移位 》》
二、位与,位或,位异或在操作寄存器时的特殊作用
1.寄存器操作的要求(特定位改变而不影响其他位)
2.特定位清零用&
3.特定位置1用|
4.特定位取反用^
三、用位运算构建特定二进制数
1.寄存器位操作经常需要特定位给特定值
2.使用位移获取特定位为1的二进制数
3.再结合位取反获取特定位为0的二进制数
4.总结:
四、位运算实战
1.例题1
2.例题2
3.例题3
4.例题4
5.例题5
6.例题6
7.例题7
8.例题8
方法1:
方法2:
五、用宏定义完成位运算
1.直接用宏来置位【赋1】,复位【赋0】(最右边为第1位)
1.1
1.2
1.3
2.截取遍历的部分连续位
一、位操作符
1.位与&
(1)位与符号是一个&,两个&&是逻辑与
(2)真值表:1&0=0 1&1=1 0&0=0 0&1=0
(3)从真值表可以看出:位与操作的特点是,只有1和1位于结果为1,其余全是0
(4)位于和逻辑与的区别:位与时两个操作数是按照二进制位批次对应位相与的,
逻辑与是两个操作数作为整体来相与的(举例:0xAA&0xF0=0xA0 ,0xAA && 0xF0=1)
2.位或|
(1)位或符号是一个|,两个||是逻辑或
(2)真值表;1|0=1 1|1=1 0|0=0 0|1=1
(3)从真值表可以看出:位或时两个操作数是按照二进制彼此对应位相与的,
逻辑或是两个操作数作为整体来相或的。
3.位取反~
(1)C语言中位取反是~,C语言中的逻辑取反是!
(2)按位取反是将操作数的二进制位逐个按位取反(1变成0,0变成1)
逻辑取反是真(在C语言中只要不是0的任何数都是真)变成假(在C语言中只有0表示假),假变成真。
#include<stdio.h>
int main() {
int a = 45;
int b, c;
b = ~a; //按位取反,逐个位操作,1变0,0变1
c = !a; //按逻辑取反【整体改变】,真变假,假变真
printf("b=%d\n", b);//-64
printf("c=%d\n", c);//0
}
#include<stdio.h>
int main() {
int a = 45;
int b, c;
b = ~(~a); //等价于~~a
c = !(!a); //等价于!!a
printf("b=%d\n", b);//45
printf("c=%d\n", c);//1
}
实验;
任何非0的数被按逻辑取反再取反就会得到1
任何非0的数被按位取反再取反就会得到它自己
4.位异或^
(1)异或真值表:1^1=0 0^0=0 1^0=1 0^1=1(记忆方法:2个数如果相等结果为0,不等结果为1)
//45:0010 1101
//^
//23:0001 0111
//= 0011 1010=58
unsigned int a = 45, b = 23;
unsigned int c;
c = a ^ b;//58
printf("c=%d\n", c);
5.位与,位或,位异或的特点总结
位与:(任何数,其实就是1和0)与1位与无变化,与0位于变成0
位或:(任何数,其实就是1和0)与1位或变成1,与0位或无变化
位异或:(任何数,其实就是1和0)与1位异或会取反,与0位异或无变化
6.左移位《《 右移位 》》
C语言的位移要取决于数据类型
对于无符号数,左移动时右侧补0(相当于逻辑移位)
对于无符号数,右移动左侧补0(相当于逻辑移位)
对于有符号数。左移时候右侧补0(角算术移位,相当于逻辑移位)
对于有符号数,右移动时候左侧补符号位(如果整数就0,负数就补1,叫算术移位)
二、位与,位或,位异或在操作寄存器时的特殊作用
1.寄存器操作的要求(特定位改变而不影响其他位)
(1)ARM是内存与IO统一编址的,ARM中有很多内部外设,SoC中的CPU通过向这些内部外设的寄存器写入一些特定的值来操控逐个内部外设,进而操控硬件动作。所以可以说,读写寄存器就是操控硬件
(2)寄存器的特定是按位进行规划和使用。但是寄存器的读写却是整体32位一起进行的。(也就是说你只想修改
(3)寄存器操作要求是:在设定特定位时不能影响其他位。
(4)如何做到?答案是:读-改-写(三部曲)。读改写的操作理念,就是:当我想改变一个寄存器中某些特定位时,我不会直接去给他写,我会先读出寄存器整体原来的值,然后再逐个基础上修改我想要修改的特定位,再将修改后的值整体写入寄存器。这样达到的效果是:再不影响其他位原来值的情况下,我关心的位的值硬件被修改了。
2.特定位清零用&
位与:(任何数,其实就是1和0)与1位与无变化,与0位于变成0
(2)如果希望将一个寄存器的某些特定位变成0而不影响其他位,可以构造一个合适的1和0组成的数和这个寄存器原来的值进行位与操作,就可以将特定位清零
(3)假设原来32位寄存器中的值为:0xaaaa aaaa,我们希望将bit8-bit15清零而其他位不变,可以将这个数与0xffff 00ff进行位与即可【想要变成0的位直接写上0,其他位写1即可】
#include<stdio.h>
int main() {
unsigned int a = 0xaaaaaaaa;
unsigned int b = 0xffffffff;
unsigned int c;
//将想要变成0的位置写上0,其他位写上1
c = a & b;
printf("c=0x%x\n", c);//0xaaaaaaaa
}
3.特定位置1用|
位或:(任何数,其实就是1和0)与1位或变成1,与0位或无变化
(2)要置1的位写上1,其他位为0,然后将这个数与相关的数进行位或
//位或:特定位置1用|
//把寄存器值的bit4-bit7置1,其他位不变
unsigned int a = 0x123d0c57;
unsigned int b = 0xf0;
unsigned int c;
c = a | b;
printf("c=0x%x\n", c);//0x123d0cf7
4.特定位取反用^
位异或:(任何数,其实就是1和0)与1位异或会取反,与0位异或无变化
(2)要取反的特定位为1,其他位取0,然后将这个数与原来的数进行位异或即可。
//异或:特定位取反用^
//把寄存器值的bit4-bit7取反,其他位不变
unsigned int a = 0x123d0cd7;
unsigned int b = 0xf0;
unsigned int c;
c = a ^ b;
printf("c=0x%x\n", c);//0x123d0c27
三、用位运算构建特定二进制数
1.寄存器位操作经常需要特定位给特定值
(1)由上节课可以找到:对寄存器特定位进行置1或者清0或者取反,关键性的难点在于要事先构建一个特定的数,这个数和原来的值进行位与,位或,位异或等操作,既可达到我们对寄存器操作的要求。
(2)解法1:用工具,难度也不大,操作起来也不是太麻烦
解法2:自己写代码用位操作符号(主要是移位和位取反)来构建这个特定的二进制数
2.使用位移获取特定位为1的二进制数
(1)最简单的是用移位来获取一个特定位为1的二进制数。比如我们需要一个bit3-bit7为1(隐含意思是其他位全部为0)的二进制数,可以
1)先写出5位为1的数(0x1f==>bit0-bit4)
2)然后将这个数左移3位(0x1f<<3==》bit3-bit7)
(2)更难一点的:获取bit3-bit7为1,同时bit23-bit25为1,其他位为0
((0x1f<<3) | (0x7<<23))
#include<stdio.h>
int main() {
//下面表达式的含义:位或说明这个数字由2部分组成,第一部分中左移2位说明第一部分从bit3开始
//第一部分数字位0x1f说明这部分有5位,所以第一部分其实是bit3到bit7
//第二部分的解读方法同样的,可知第二部其实是bit23到bit25
unsigned int a;
a = ((0x1f << 3) | (0x7 << 23));
printf("a=0x%x\n", a);//a=0x38000f8
return 0;
}
3.再结合位取反获取特定位为0的二进制数
(1)现在我们要获取bit4-bit10为0,其余位全部为1的数。怎么做?
(2)利用上面讲的方法就可以:(0xf<<0) | (0x1fffff<<11)
unsigned int a;
a = ((0xf << 0) | (0x1fffff << 11));
printf("a=0x%x\n", a);//a=0xfffff80f
return 0;
(3)但是这些存在问题:连续为1的位数太多了,这个数字本身就很难构造,所以这种方法的优势损失了
(4)思路是:先试图构造出这个数的位相反数,再取反得到这个数。(比如本例中要构造的数bit4-bit10为0,其他位为1),那我们先构造出一个bit4-bit10为1,其他位为0的数,然后对这个数按位取反即可。
//使用位移+取反
unsigned int a;
a = ~(0x7f<<4);
printf("a=0x%x\n", a);//a=0xfffff80f
return 0;
4.总结:
(1)如果你要的这个数比较少位为1,大部分位为0,则可以通过连续很多个1左移n位得到
(2)如果你想要的这个数是比较少位为0,大部分为1,则可以通过先根据其位反数,然后再位取反来得到
(3)如果你想要得到的数中连续1(连续0)的部分不止1个,那么可以通过多段分别构建,然后再彼此位与【|】即可。这个时候因为参与位或运算的各个数为1的位是不重复的,所以这时候的位或其实相当于几个数的叠加。
四、位运算实战
要置1用|,用清零用&,要取反用^,~和《《 》》用来构建特定二进制数。
1.例题1
给定一个整形数a,设置a的bit3,保证其他位不变。
a=a|(1<<3) 或者 a|=(1<<3)
2.例题2
给定一个整形数a,设置a的bit3-bit7,保证其他位不变。
a=a|(0x1f<<3) 或者 a|=0x1f
3.例题3
给定一个整形数a,清除a的bit15,保证其他位不变。
a=a& ~(0x1<<15) 或者 a&=~(0x1<<15)
4.例题4
给定一个整形数a,清除a的bit15-bit23,保证其他位不变。
a=a&(~(0x1ff<<15)) 或者 a&=(~(0x1ff<<15))
5.例题5
给定一个整形数a,取出a的bit3-bit8
思路:
第一步:先讲这个数bit3-bit8不变,其他位全部清零--->a=a&(0x3f<<3)
第二步:再讲其右移3位得到结果
//第一步:把bit3到bit8的位保持不变,其余位全部清零
a=a& (0x3f<<3)
//第二步:将其右移3位
a>>=3;
6.例题6
将一个寄存器的bit7-bit17中的值赋值937(其余位不受到影响)
关键点:不能影响其他位,并不知道原来bit7-bit17中装的值
思路
第一步:先将bit7-bit18全部清零,当然不能影响其他位
第二部:将bit7-bit17进行赋值【使用”|“因为我们要赋值】
//第一步:bit7-bit17全部清零
a&=~(0x7ff<<7);
//第二步:bit7-bit17赋值937
a|=(937<<7);
7.例题7
将一个寄存器的bit7-bit17中的值+17(其余位不受到影响)
关键点:不知道原来的值是多少,
思路:
第一步:先读出原来的bit7-bit17的值
第二步:给这个值加17
第三步:将bit7-bit17清零
第四步:将第二步算出来的值写入bit7-bit17
unsigned int a=0xc30288f8;
//第一步:先读出原来的bit7-bit17的值
unsigned int tmp;
tmp=a&(0x3ff<<7);//使用【&】和全1将这10位数进行计算
tmp=tmp>>7//将其移动到低位取出
//第二步:给这个值加17
tmp+=17;
//第三步:将bit7-bit17清零
a=a & ~(0x3f<<7);
//第四步:将第二步算出来的值写入bit7-bit17
a=a| (tmp<<7);
8.例题8
将一个寄存器的bit7-bit17中的值赋值937(其余位不受到影响),同时个bit21-bit25赋值17
方法1:
unsigned int a;
unsigned int tmp;
//第一步:先将bit7-bit17位全部置为0
a=a&~(0x7ff<<7);
//第二步:将这几位进行赋值
a=a|(937<<7);
//第三步:先取出bit21-bit25
tmp=a&(0x1f<<21);
tmp=tmp>>21;
//第四步:将17加上
tmp+=17;
//第五步:将bit21-bit25置为0
a=a&~(0x1f<<21);
//第六步;将tmp放回到bit21-bit25
a=a|(tmp<<21);
方法2:
unsigned int a=0x30288f8;
a&=~((0x3ff<<7) | (0x1f<<21));//bit7-bit17和bit21-bit25全部清零
a|=((937<<7) | (17<<21));//937和17全部赋值
五、用宏定义完成位运算
1.直接用宏来置位【赋1】,复位【赋0】(最右边为第1位)
1.1
用宏定义将32位数的第n位(右边其算,也就是bit0是第二位)置位【变成1】
//第一题:用宏定义将32位数的第n位(右边其算,也就是bit0是第二位)置位【变成1】
//1U:表示无符号1---》(x | (1U <<(n-1)))
#define SET_BIT_N(x,n) (x | (1<<(n-1)))//因为我们是从bit0开始算的
int main() {
unsigned int a = 0;
unsigned int b = 0;
b=SET_BIT_N(a, 4);//对应bit3
printf("0x%x", b);
}
1.2
第二题:用宏定义将32位数x的第n位(右边其算,也就是bit0是第1位)清零【变成0】
#include<stdio.h>
//第二题:用宏定义将32位数x的第n位(右边其算,也就是bit0是第1位)清零【变成0】
#define CLEAR_BIT_N(x,n) (x&~(1U<<(n-1)))
int main() {
//第二题
unsigned int a = 0xffff;
unsigned int b = 0;
b= CLEAR_BIT_N(a, 4);//对应bit3
printf("0x%x", b);//0xfff7
}
1.3
用宏定义将32位数x的第n位到第m位(右边起算,也就是bit0是第1位)置位【变成1】
我们需要一个算式得出(m-n+1)个1
算法:第一步:先得到32位1----》~(0U)
第二步:将第一步得到的数右移x位即可得到(m-n+1)个1-----》~(0U)>>(32-(m-n+1))
第三步:将得出的结果移回去原来的位置
//第三题:用宏定义将32位数x的第n位到第m位(右边起算,也就是bit0是第1位)置位【变成1】
//我们需要一个算式得出(m-n+1)个1
//算法:第一步:先得到32位1----》~(0U)
// 第二步:将第一步得到的数右移x位即可得到(m-n+1)个1-----》~(0U)>>(32-(m-n+1))
// 第三步:将得出的结果移回去原来的位置
#define CLEAR_BIT_N_M (x,n,m) (x| (((~0U)>>(32-(m-n+1)))<<(n-1)
//第三题
unsigned int a = 0x0;
unsigned int b = 0;
b = CLEAR_BIT_N_M(a,5,8);//对应bit3
printf("0x%x", b);//
2.截取遍历的部分连续位
#define GETBITS(x,n,m) ((x&~(~0U) << (m-n+1)) <<(n-1)) >> (n-1))