C语言位操作

C语言位操作复习

一、基本用法

  1. 位与& “位与”就是把2个十六进制的数先分别转换为二进制,然后再相与。而“逻辑与”就是把2个十六进制分别看成2个整体,2个整体相与。
    “位或”就是把2个十六进制的数先分别转换为二进制,然后再位或;而“逻辑或”就是把2个十六进制分别看成2个整体,2个整体位或。

  2. 位取反~
    “位取反”就是把十六进制数先转换为二进制,然后把每一位取反(1取反就变为0,0取反就为1);而“逻辑取反”就是把十六进制数看成一个整体,然后取反(非0的数都是为真,逻辑取反后为假;0逻辑取反为真)。

  3. 位异或^ 例子: 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

  4. 位左移<< 位右移>> 操作的时候要考虑有符号数(signed number)和无符号数(unsigned number)。
    对于有符号数:左移的时候右侧补0;右移的时候左侧补符号位(正数符号位为0,则补0;负数符号位为1,则补1).
    对于无符号数:左移的时候右侧补0;右移的时候左侧也是补0. 注意:我们对寄存器进行赋值的时候用的都是无符号的数

  5. 优先级: 按位取反(~) > 按位左/右移(<</>>) > 按位与(&) > 按位异或(^) > 按位或(|)

二.位操作的实际应用

一般在操作寄存器的时候使用,例如GPIO的各个pin引脚,有时候你只想改变某个pin脚的值从而实现某项功能,其余pin脚保持不变,就得使用位操作,只对目标位进行操作。

对寄存器的操作方式是:读->改->写。

那么为啥这样做呢?为啥不能直接给寄存器写值呢?
因为你只知道要把目标位设置为某值,但是其他的位你并不知道原本是多少,所以要先读取这个寄存器的整体值,然后再修改其中的目标位,然后再把修改后的值写到寄存器。

三、一些常见用法

  1. 特定位清零用&

    譬如:将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
    
  2. 特定位 置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
    
  3. 特定位取反用 ^
    譬如:将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
    
  4. 特定二进制数的获取
    由于上面示例中的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;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值