C语言位操作复习
一、基本用法
-
位与& “位与”就是把2个十六进制的数先分别转换为二进制,然后再相与。而“逻辑与”就是把2个十六进制分别看成2个整体,2个整体相与。
“位或”就是把2个十六进制的数先分别转换为二进制,然后再位或;而“逻辑或”就是把2个十六进制分别看成2个整体,2个整体位或。 -
位取反~
“位取反”就是把十六进制数先转换为二进制,然后把每一位取反(1取反就变为0,0取反就为1);而“逻辑取反”就是把十六进制数看成一个整体,然后取反(非0的数都是为真,逻辑取反后为假;0逻辑取反为真)。 -
位异或^ 例子: 66 ^ 33 = 99
66: 1 0 0 0 0 1 0
33: 0 1 0 0 0 0 1
99: 1 1 0 0 0 1 1 -
位左移<< 位右移>> 操作的时候要考虑有符号数(signed number)和无符号数(unsigned number)。
对于有符号数:左移的时候右侧补0;右移的时候左侧补符号位(正数符号位为0,则补0;负数符号位为1,则补1).
对于无符号数:左移的时候右侧补0;右移的时候左侧也是补0. 注意:我们对寄存器进行赋值的时候用的都是无符号的数 -
优先级: 按位取反(~) > 按位左/右移(<</>>) > 按位与(&) > 按位异或(^) > 按位或(|)
二.位操作的实际应用
一般在操作寄存器的时候使用,例如GPIO的各个pin引脚,有时候你只想改变某个pin脚的值从而实现某项功能,其余pin脚保持不变,就得使用位操作,只对目标位进行操作。
对寄存器的操作方式是:读->改->写。
那么为啥这样做呢?为啥不能直接给寄存器写值呢?
因为你只知道要把目标位设置为某值,但是其他的位你并不知道原本是多少,所以要先读取这个寄存器的整体值,然后再修改其中的目标位,然后再把修改后的值写到寄存器。
三、一些常见用法
-
特定位清零用&
譬如:将0xAAAAAAAA 的bit8 ~bit15清零,其他位保持不变。
分析:[位与]任何数(0/1)与1位与时为本身,与0位与为0,所以可以用位与的方式。
unsigned int a = 0xAAAAAAAA; unsigned int b = 0xFFFF00FF; unsigned int c; c = a & b; printf("c = 0x%x ", c); //c = 0xaaaa00aa
-
特定位 置1用 |
譬如:将0xffff00ff的bit8 ~bit15置1,其他位保持不变。
分析:位或 任何数(0/1)与1位或变为1,与0位或为本身
unsigned int a = 0xffff00ff; unsigned int b = 0x000ff00; unsigned int c; c = a | b; printf("c = 0x%x ", c); // = 0xffffffff
-
特定位取反用 ^
譬如:将0xffff00ff的bit8 ~bit15取反,其余保持不变
分析:位异或 任何数(0/1)与1位异或会取反,与0位异或为本身unsigned int a = 0xffff00ff; unsigned int b = 0xff00; unsigned int c; c = a ^ b; printf("c = 0x%x ", c); // = 0xffffffff
-
特定二进制数的获取
由于上面示例中的unsigned int b的值太过于呆板,后续看代码的时候可能不知道为什么是这个值,所以需要以另外的方式来表达(主要是通过位移和位取反来获取这个特定的二进制数)。
(1)譬如:unsigned int b = 0xff00; 中的0xff00
可表示为:
0xFF<<8 (0xFF :8个二进制1 <<: 左移 8: 8位) //这就像我们事先准备好一个包,然后将这个包通过位移, 移到特定的位置,再进行后续操作。
(2) 需要一个bit3 ~bit7,
bit23~bit25为1的数 (隐含意思是其他位都为0)
可为:
0x1F<<3 | 0x7<<23
1F (7-3+1=5个1,转换为十六进制则为1F);
7 ( 25-23+1=3个1,转换为十六进制则为7)
| ( 位或,任何数与1位或都为1,与0位或为本身,所以这里用位或就相当于把这2个位移后的数叠加起来)
(3) 需要一个bit3 ~ bit7为0的数
可为:
~(0x1F<<3)
四.代码实战
1、给定一个整型数a,设置a的bit3,保证其他位不变
注意:设置就是设置为1的意思,清除就是清除为0的意思
unsigned int a = 0xc3057ad3;2
a |= (1<<3);3
printf("a = 0x%x.\n", a); //a = 0xc3057adb.
2、给定一个整形数a,设置a的bit3~bit7,保持其他位不变
unsigned int a = 0xc3057ad3;2
a |= (0x1f<<3);3
printf("a = 0x%x.\n", a); //a = 0xc3057afb.
3、给定一个整型数a,清除a的bit15,保证其他位不变。
unsigned int a = 0xc305bad3;2
a &= ~(1<<15);3
printf("a = 0x%x.\n", a); //a = 0xc3053ad3.
4、给定一个整形数a,清除a的bit15~bit23,保持其他位不变。
unsigned int a = 0xc305bad3;2
a &= ~(0x1ff<<15);3
printf("a = 0x%x.\n", a); //a = 0xc3003ad3.
5、给定一个整形数a,取出a的bit3~bit8。
注意:要取出这个数,首先要把其他位清零&,之后右移就可以得出那个数了
unsigned int a = 0xc305bad3
unsigned int tmp;
tmp = a & (0x3f<<3);
tmp >>= 3;
printf("tmp = 0x%x.\n", tmp);
6、用C语言给一个寄存器的bit7~bit17赋值937(其余位不受影响)。
注意:要赋值就需要把要赋值的那几位清零&,然后把需要赋值的数左移到那几位上就可以了
unsigned int a = 0xc305bad3;
a &= (~(0x7ff<<7));
a |= (937<<7);
printf("a = 0x%x.\n", a); //a = 0xc305d4d3.
7、用C语言将一个寄存器的bit7~bit17中的值加17(其余位不受影响)。
注意:要加值,就要把那几位的数字取出来,然后把那几位清零&,之后右移,相加,左移,再置1 |
unsigned int a = 0xc305bad3;
unsigned int tmp;
tmp = a & (0x7ff<<7);
a &= (~(0x7ff<<7));
tmp >>= 7;
tmp += 17;
tmp <<= 7;
a |= tmp;
printf("a = 0x%x.\n", a); //a = 0xc305c353
8、用C语言给一个寄存器的bit7~bit17赋值937,同时给bit21~bit25赋值17.
注意:同时赋值和一个个来赋值是一样的,就是多了几步而已
unsigned int a = 0xc305bad3;
a &= (~(0x7ff<<7));
a |= (937<<7);
a &= (~(0x1f<<21));
a |= (17<<21);
printf("a = 0x%x.\n", a); //a = 0xc225d4d3.
不同版本
unsigned int a = 0xc305bad3;
a &= ((~(0x7ff<<7)) & (~(0x1f<<21)));
a |= ((937<<7) | (17<<21));
printf("a = 0x%x.\n", a); //a = 0xc225d4d3.
五.用宏定义来实现位操作
其实目的就是把几行的代码写到一行去(难度也是在这里),我们直接看代码.
#define SET_BIT(x,n) (x | 1U<<(n-1))
//这里就是要把数字x的第n位(bit(n-1)位)置为1
//1U就表示的是无符号的1,宏定义可以传参的
#define CLEAR_BIT(x,n) (x & ~(1U<<(n-1)))
//这里就是要把数字x的第n位(bit(n-1)位)清零
#define SET_BITS(x,n,m) (x | ~(~0U<<(m-n+1))<<(n-1))
//这里就是要把数字x的第n到m位置为1(n是低位,m是高位)
//就是先把0取反就可以得到很多的1,然后左移就得到那么多个0,再取反就可以得到你想要的1的个数了
//最后左移位或就可以置1了
#define GET_BIT(x,n,m) (x & ~(~0U<<(m-n+1))<<(n-1)) >>(n-1)
//截取变量的部分连续位。(就是取出的意思)
//其实和上面那里是差不多的,后面那一大部分都是为了确定需要多少个1
//最后记得右移,为了得出那个数字
int main (void)
{
unsigned int a = 0xfffffabf;
unsigned int b;
b = GET_BIT(a,5,12); //上面那里就是ab
printf("b = 0x%x.\n", b); //b = 0xab.
/*
unsigned int a = 0x0;
unsigned int b;
b = SET_BITS(a,5,8);
printf("b = 0x%x.\n", b); //b = 0xf0.
*/
/*
unsigned int a = 0xf;
unsigned int b = 0;
b = CLEAR_BIT(a,4);
printf("b = 0x%x.\n", b); //b = 0x7.
*/
/*
unsigned int a = 0;
unsigned int b = 0;
b = SET_BIT(a,4);
printf("b = 0x%x.\n", b); //b = 0x8.
*/
return 0;
}