C语言位操作

C语言中位操作

注:本文内容源自朱有鹏老师C语言专题精讲-C语言位操作部分

阅读目录:
1、常用的位运算符
2、特定位操作
3、宏定义完成位运算

1、常用的位运算符:
1.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)

1.2、位或|:

(1)注意:位或符号是一个|,两个||是逻辑或。
(2)真值表:1|0=1 1|1=1 0|0=0 0|1=1
(3)从真值表可以看出:位或操作的特点是:只有2个0相位或才能得到0,只要有1个1结果就一定是1.
(4)位或和逻辑或的区别:位或时两个操作数是按照二进制位彼次对应位相与的,逻辑或是两个操作数作为整体来相或的。

1.3、位取反~:

(1)注意:C语言中位取反是~,C语言中的逻辑取反是!
(2)按位取反是将操作数的二进制位逐个按位取反(1变成0,0变成1);
  而逻辑取反是真变成假(在C语言中只有0表示假)、假变成真。

实验:任何非0的数被按逻辑取反再取反就会得到1; 任何非0的数倍按位取反再取反就会得到他自己;

// 按位和按逻辑取反再取反
    unsigned int a = 45;
    unsigned int b, c;
    b = ~~a;                      // 按位取反,逐个位操作,1变0,0变1
    c = !!a;                        // 按逻辑取反,真变假,假变真
    printf("b = %u.\n", b);
    printf("c = %d.\n", c);

1.4、位异或^:

(1)位异或真值表:1^1=0 0^0=0 1^0=1 0^1=1
(2)位异或的特点:(任何数,其实就是1或者0)与1位异或会取反,与0位异或无变化

1.5、左移位<< 与右移位>>:

C语言的移位要取决于数据类型。
对于无符号数,左移时右侧补0(相当于逻辑移位)
对于无符号数,右移时左侧补0(相当于逻辑移位)
对于有符号数,左移时右侧补0(叫算术移位,相当于逻辑移位)
对于有符号数,右移时左侧补符号位(如果正数就补0,负数就补1,叫算术移位)


2、特定位操作:

2.1、特定位置1用|:

(1)位或操作的特点:任何数(其实就是1或者0)与1位或变成1,与0位或无变化
(2)我们构造一个数:要置1的特定位为1,其他位为0,然后将这个数与原数位或即可。

2.2、特定位清零用&:

(1)任何数与1位与无变化,与0位与变成0
(2)可以构造一个合适的1和0组成的数和原来的值位与操作,就可以将特定位清零。
(3)举例:假设原来32位寄存器中的值为:0xAAAAAAAA,我们希望将bit8~bit15清零而其他位不变,可以将这个数与0xFFFF00FF进行位与。

2.3、特定位取反用^:

(1)(任何数,其实就是1或者0)与1位异或会取反,与0位异或无变化
(2)构造特定数:要取反的特定位为1,其他位为0,然后将这个数与原来的数进行位异或

2.4、构建特定二进制数:

(1)对寄存器特定位进行置1或者清0或者取反,关键难点在于要事先构建一个特别的数,这个数和原来的值进行位与、位或、位异或等操作,即可达到我们对寄存器操作的要求。
(2)如果你要的这个数比较少位为1,大部分位为0,则可以通过连续很多个1左移n位得到。
(3)如果你想要的数是比较少位为0,大部分位为1,则可以通过先构建其位反数,然后再位取反来得到。
(4)如果你想要的数中连续1(连续0)的部分不止1个,那么可以通过多段分别构造,然后再彼此位与即可。这时候因为参与位或运算的各个数为1的位是不重复的,所以这时候的位或其实相当于几个数的叠加。

3、宏定义完成位运算:

3.1、宏定义置位操作:

#define SET_NTH_BIT(x, n) (x | ((1U)<<(n-1)))
分析:用宏定义将32位数x的第n位(右边起算,也就是bit0算第1位)置位
即将无符号数1左移n-1位,得到10000000000000…,与原数值取或,完成操作。

3.2、宏定义复位操作:

#define CLEAR_NTH_BIT(x, n) (x & ~((1U)<<(n-1)))
分析:用宏定义将32位数x的第n位(右边起算,也就是bit0算第1位)清零
即将其特定位与0取与,将1左移n-1位取反,可以得到011111…,与原数值取与,完成操作

3.3、宏定义将32位数x的第n位到第m位置位:

#define SET_BIT_N_M(x, n, m) (x | (((~0U)>>(32-(m-n+1)))<<(n-1)))
分析:
右边起算,也就是bit0算第1位,m是高位置位
我们需要一个算式来得到(m-n+1)个1
第1步:先得到32位1: ~0U
第2步:将~0U右移(32-(m-n+1))位即可得到(m-n+1)个1 即: (~0U)>>(32-(m-n+1))
第3步:将第2步得到的数左移n-1位,取或即可<<(n-1)
完整:#define SET_BIT_N_M(x, n, m) (x | (((~0U)>>(32-(m-n+1)))<<(n-1)))

3.4、宏定义截取变量x的第n到第m位:

#define GETBITS(x, n, m) ((x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-1))

分析:这个题目相当于是要把x的bit(n-1)到bit(m-1)取出来
即将bitn~bitm不变,其余位清零,再将其右移n-1位

复杂宏怎么分析:
((x & ~(~(0U)<<(m-n+1))<<(n-1)) >> (n-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)
这个分析时要搞清楚第2坨到底应该先左边取反再右边<<还是先右边<<再左边取反。自己实际写个代码测试,这个式子应该是 ~(~(0U)<<(m-n+1)) << (n-1) ,这就又分为2部分了~(0U)<<(m-n+1)) 与<< (n-1)

// 写代码测试 ~和 << 谁的优先级高
    unsigned int a = 0xf;
    unsigned int b = 0;
    b = ~a<<4;                 // 如果先~结果是:0xFFFFFF00  如果先<<结果是:0xffffff0f
    printf("b = 0x%x.\n", b);   // 结果是0xffffff00,说明~优先级高

你可能感兴趣的:(C/C++)