4.2、C语言的位操作

4.2.1、位操作符

    注意:C语言中位取反是~,逻辑取反是!;
    任何非0的数被按逻辑取反再取反就会得到1:,任何非0的数被按位取反再取反就会得到它自己。
#include<stdio.h>

int main(void)
{
    unsigned int a=45;
    unsigned int b,c;
    b = ~~a;    //按位取反
    c = !!a;  //逻辑取反    //逻辑取反只有0和1
    printf("b=%u.\n",b);
    printf("c=%d.\n",c);
    
    return 0;
}

    对于有符号数(算术移位),右移时左侧补符号位(如果是整数就补0,如果是负数就补1,叫算术移位)。异或:(1)位异或真值表:1^1=0     0^0=0    1^0=1    0^1=1   。
移位:对于无符号数,左移时右侧补0(相当于逻辑移位)
   对于无符号数,右移时左侧补0(相当于逻辑移位)
          对于有符号数,左移时右侧补0(叫算术移位,相当于逻辑移位)
          对于有符号数,右移时左侧补符号位(如果正数就补0,负数就补1,叫算术移位)
嵌入式中研究的移位,以及使用的移位都是无符号数

(2)寄存器的特点是按位进行规划和使用。但是寄存器的读写却是整体32位一起进行的(也就是说你只想修改bit5~bit7是不行的,必须 整体 32bit全部写入)

4.2.3.2、使用移位获取特定位为1的二进制数
(1)最简单的就是用移位来获取一个特定位为1的二进制数。譬如我们需要一个bit3~bit7为1(隐含意思就是其他位全部为0)的二进制数,可以这样:(0x1f<<3)(7-3+1 == 0x1f)

(2)更难一点的要求:获取bit3~bit7为1,同时bit23~bit25为1,其余位为0的数:((0x1f<<3) | (7 << 23))   

4.2.3.3、再结合位取反获取特定位为0的二进制数
(1)这次我们要获取bit4~bit10为0,其余位全部为1的数。怎么做?
~(0x7f<<4)

(2)利用上面讲的方法就可以:(0xf<<0)|(0x1fffff<<11)
但是问题是:连续为1的位数太多了,这个数字本身就很难构造,所以这种方法的优势损失掉了。

(3)这种特定位(比较少)为0而其余位(大部分)为1的数,不适合用很多个连续1左移的方式来构造,适合左移加位取反的方式来构造。

(2)思路是:先试图构造出这个数的位相反数,再取反得到这个数。(譬如本例中要构造的数bit4~bit10为0其余位为1,那我们就先构造一个bit4~bit10为1,其余位为0的数,然后对这个数按位取反即可)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char  **argv)
{
/*
    // 把一个寄存器值的bit3~bit7取反,其他位不变
    //异或是拿来取反用的。
    unsigned int a;
    unsigned int b = 0x12ff34ff;
    unsigned int c = 0xf0;
    a = (b ^ c);
    printf("a = %x\n", a);
    //异或是拿来取反用的。1是取反,0不变
    
*/    
/*
    // 把一个寄存器值的bit4~bit7置1,其他位不变
    //首先7-4=3+1 = 4;对应的十六进制就是:0xf
    unsigned int a;
    unsigned int b;
    unsigned int c;
    c = 0xf;
    a = 0xf << 4;
    printf("a = %x\n", a);
*/    
    
    //下面的内容是:4.2.3、章节的内容
/*    
    //譬如我们需要一个bit3~bit7为1(隐含意思就是其他位全部为0)的二进制数
    unsigned int a;
    //首先 7-3 + 1 = 5 位:        十六进制:1f
    a = 0x1f << 3;
    printf("a = %x\n", a);
*/
/*
    //获取bit3~bit7为1,同时bit23~bit25为1,其余位为0的数
    unsigned int a;
    //首先计算出    7-3+1 = 5位,,,1f        |    25 - 23 + 1 = 3 。。。7
    a = (0x1f<<3) | (0x7<<23);
    printf("a = %x\n", a);
*/    
/*
    为什么一个是移动11,一个是移动4。???因为是除去bit0计位移动的
*/
/*    
    //这次我们要获取bit4~bit10为0,其余位全部为1的数。
    unsigned int a;
    a = (0xf<<0) | (0x1fffff<<11);    //    a = fffff80f    第二个数需要移11位。这个方法比较蠢
    printf("a = %x\n", a);
*/    
    //聪明的方法,需要的是大部分为1,特定位为0的数可以先获得特定位为1,然后再取反
    unsigned int a;
    a =    ~(0x7f<<4);                    //先bit4~bit10为1,然后去反。7位 0x7f
    printf("a = %x\n", a);            //a = fffff80f
    exit(0);
}

4.2.3.4、总结:位与、位或结合特定二进制数即可完成寄存器位操作需求
(1)如果你要的这个数比较少位为1,大部分位为0,则可以通过连续很多个1左移n位得到。

(2)如果你想要的数是比较少位为0,大部分位为1,则可以通过先构建其位反数,然后再位取反来得到。

(3)如果你想要的数中连续1(连续0)的部分不止1个,那么可以通过多段分别构造,然后再彼此位或即可。这时候因为参与位或运算的各个数为1的位是不重复的,所以这时候的位或其实相当于几个数的叠加。

4.2.2、位与位或位异或在操作寄存器时的特殊作用     (***)

    1、特定位清零用&,2、特定位置1用|,3、要取反用位异或^: (任何数,其实就是1或者0)与1位异或会取反,与0位异或无变化。
~和<< >>用来构建特定二进制数。

    1、将一个bit3~bit7置1,同时将bit11~bit20置一::((0x1f<<3)|(0x3ff<<11))构建特定二进制数。

    2、用C语言给一个寄存器的bit7~bit17赋值937:假定a=0xffffffff      (思路)  赋值:即消除原来的数,将新的值写入

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//回顾:要置1用|,用清零用&,要取反用^,~和<< >>用来构建特定二进制数。
#define VALUE1 937
#define VALUE2 17
int main(int argc, char **argv)
{
/*
    //给定一个整形数a,设置a的bit3,保证其他位不变。
    unsigned int a = 0xffffffff;
    a |= (1<<3);
    printf("a = %x\n", a);
*/
    
/*    //给定一个整形数a,设置a的bit3~bit7,保持其它位不变    7-3+1 = 5;    (0b,表示的是二进制)
    unsigned int a = 0xffffffff;
    a |= (0x1f<<3);
    printf("a = %x\n", a);
*/
/*
    //给定一个整形数a,清除a的bit15,保证其他位不变。
    unsigned int a = 0xffffffff;
    a &= (~(0x1<<15));
    printf("a = %x\n", a);
*/
/*
    //给定一个整形数a,清除a的bit15 ·~ bit 23,保证其他位不变。
    //思路:先构建为1的二进制位,再取反就可以得到目标位。23 - 15 + 1 = 9
    unsigned int a = 0xffffffff;
    a &= (~(0x1ff<<15));
    printf("a = %x\n", a);
*/
/*
    //给定一个整形数a, 取出a的bit3到bit8位。4.2.4.5
    //思路:1.将bit3到bit8的其余位都清零(并不是指定位清零,所以不需要取反),2然后右移3位将目标数移出来。
    unsigned int a = 0xffffffff;
    a &= (0x3f<<3);
    a >>= 3;
    printf("a = %x\n", a);
*/
/*
    //用C语言给一个寄存器的bit7~bit17赋值937(其余位不受影响)
    //思路 1、先将bit7~bit17清零,2、然后左移7位赋值
    unsigned int a = 0x00023480;
    a &= (~(0x7ff<<7));
    //a = (a | (937<<7));
    a |= (937<<7);
    printf("a = %x\n", a);            //如何验证,将结果右移7位,选中bit7~bit17,转成十进制看是否为937.
*/
/*
    //用C语言将一个寄存器的bit7~bit17中的值加17(其余位不受影响)
    //思路1、将bit7~bit17中的数右移七位,移出来。(这里用利用一个临时变量。)
    //然后2、将这个移出来的数+17,
    //然后3、将bit7~bit17的位数清零。
    //然后4、将这个+17的数左移7位(位或)放进去
    unsigned int a = 0xfff0000f,temp;
    temp = a >> 7;                    //注意细节的问题,调试的功底。
    temp += 17;
    a &= (~(0x7ff<<7));
    a |= (temp << 7);
    printf("a = %x\n", a);        //如何验证是否错误。将得到的数 - 原来的数,在右移7位,看结果是否为17
*/
    //用C语言将一个寄存器的bit7~bit17赋值937,同时给bit21~bit25赋值17.
//    unsigned int a = 0x5870000f;
/*    //思路1、先将bit7~bit17清零,2、然后将937左移7位移进去
    a &= ~(0x7ff<<7);
    a |= (937<<7);
    
    a &= (~(0x1f << 21));
    a |= (17<<21);
    printf("a = %x\n", a);        //a = 5a31d48f
*/
/*
    //第二种方法。。
    a &= (~((0x7ff<<7)|(0x1f<<22)));
    a |= ((VALUE1<<7)|(VALUE2<<21));
    printf("a = %x\n", a);    
*/    
    //题目二:用C语言将一个寄存器的bit7~bit17加937,同时给bit21~bit25赋值17.(不影响其他位)
    unsigned int a = 0x5870000f,temp1,temp2;
    //思路:1、先将bit17~bit7位向右移七位,
           //2、然后加937,
           //3、将bit7~bit17位清零
           //4、将加完的数左移7位写进去。
    temp1 = a>>7;
    temp1 += 937;
    a &= ~(0x7ff<<7);
    a = a | (temp1<<7);            //a加上移位过的数,|的作用是将其余位添加进来。
    
    //1、先给bit21~25清零
    //2、再将17左移21位放进去
    a &= ~(0x1f<<21);
    a |= (17<<21);
    printf("a = %x\n", a);    
    
    exit(0);
    
}

    第一步,先将bit7~bit17全部清零,不能影响其他位::a&=~(0x7ff<<7)';
    第二步,将937写入bit7~bit17即可::  a|=(937<<7);;;
/      3、用C语言将一个寄存器的bit7~bit17中的值加17(其余位不受影响)。
    第一步,将bit7~bit17中的值取出来 temp = a&(0x7ff<<7);;取出 temp  >>  =7
    第二步,将取出来的值加17: temp  += 17;
    第三步,将a的bit7~bit17的值清零   a  &=  ~(0x7ff<<7)
    第四步,将第二步的temp写入到原来a中::  a |= (temp<<7);
    用C语言给一个寄存器的bit7~bit17赋值937,同时给bit21~bit25赋值17.
    解法一:double第二题。解法二: 思路:先将bit7~bit17和bit21~bit25同时清零,然后再赋值(即同时左移相应位数)。
    a&=~((0x7ff<<7)|(0x1f<<21));             然后:a |= ((937<<7)|(17<<21))
技术升级宏定义:(掌握这种分析方法,解决思路。)
    详细内容见代码部分。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//第一题:用宏定义将32位数x的第n位(右边起算,也就是bit0算第一位)置位1
#define  SET_BIT_N(x, n)    (x | (1U<<(n-1)))    //如果是第3位,就是bit2,所以 n - 1
//    (n-1)的作用是需要移的bit位数    1U的作用是需要置位的是1,并且加了U考虑到有符号时的右移情况。
//第二题:用宏定义将32位数x的第n位(右边起算,也就是bit0算第1位)清零
#define CLEAN_BIT_N(x, n)    (x & (~(1U<<(n-1))))
//第三题:宏定义将32位数x的第n位到第m位(右边起算,也就是bit0算是第一位,m是高位)置位1
//分析假如n = 3, m = 6 ,就是要求将bit2~bit5位置位1
//思路1、我们需要一个算式来得到(m-n+1)个1
//算法:1、先得到32位1:    ~0U
//        2、将第一步得到的数右移 32-(m- n +1)位,即可以得到m-n+1个位数的1    ~0U >> (32-(m-n+1))
#define SET_BIT_N_M(x, n, m)    (x | (~0U >> (32-(m-n+1)) << (n-1)))
//第四题:截取变量x的第n到第m位。
//截取变量的部分连续位。例如:变量0x88,也就是10001000B,若截取2~4位,则值为:100B = 4
#define    GETBITS(x, n, m)    ((x & ~(~(0U)<<(m-n+1))<<(n-1))>>(n-1))
//分析这个宏:这个题目相当于我们下面的例子,这个题目相当于是要把x的bit(n-1)到bit(m-1)位取出来。
/*
    //给定一个整形数a, 取出a的bit3到bit8位。4.2.4.5
    //思路:1.将bit3到bit8的其余位都清零(并不是指定位清零,所以不需要取反),2然后右移3位将目标数移出来。
    unsigned int a = 0xffffffff;
    a &= (0x3f<<3);
    a >>= 3;
    printf("a = %x\n", a);
*/
//复杂宏的分析方法:(掌握这种分析方法,解决思路。)
//1、第一步:先分清楚这个复杂宏分为几部分:2部分
//x & ~(~(0U)<<(m-n+1))<<(n-1)                >>(n-1):这里为什么要右移n-1位,因为需要将数字取出来。
//第二步:继续解析剩下的:又分为2部分。
//x & ~(~(0U)<<(m-n+1))             <<(n-1)
//第三步:~(~(0U)<<(m-n+1))               <<(n-1)
//这个的作用是~(~(0U)<<(m-n+1))   和    (~0U >> (32-(m-n+1))  的作用是相同的
int main(int argc, char **argv)
{
/*
    //验证第一题。
    unsigned int a = 0x0f;
    unsigned int b = 0;   //用一个数来接收宏返回的数
    b = SET_BIT_N(a, 7);
    printf("b = %x\n", b);
*/
/*
    //验证第二题
    unsigned int a = 0x0f;
    unsigned int b = 0;    //定义一个数用来接收宏返回的数
    b = CLEAN_BIT_N(a, 4);
    printf("b = %x\n", b);
*/
    //验证第三题
    unsigned int a = 0x0f;
    unsigned int b = 0;    //定义一个数用来接收宏返回的数
    b = SET_BIT_N_M(a, 5, 8);
    printf("b = %x\n", b);
    
    
    exit(0);
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值