C中的位操作讲解详细(转载)

C语言位操作
1、位操作符
(1)位与& 逻辑与&& 1&1=1 1&0=0 0&0=0 0&1=0

(2)位或 | 逻辑或 || 1 | 1=1 1 | 0=1 0 | 0=0 0 | 1=1

(3)位取反~ 逻辑取反 !

(4)位异或 ^ 不同为1,相同为0 1^1=0 1^0=1 0^0=0 0^1=1

总结:

位操作是按照二进制数的每一位进行操作的,逻辑操作是对数的整体操作的
位与,与1位与无变化,与0位与为0
位或,与1位或为1,与0位或无变化
位异或,与1位异或会取反,与0位异或无变化
(5)移位操作

C语言的移位要取决于数据类型

左移位 << 右移位 >>

无符号数, 逻辑移位

左移位时右侧补0

右移位时左侧补0

有符号数, 算数移位

左移位时右侧补0(相当于逻辑移位)

右移位时左侧补符号位,如果是正数就补0,负数就补1(第一位为符号位,0为正,1为负)

2、位操作符在操作寄存器时的特殊作用
(1)寄存器的操作要求
ARM是内存和IO统一编址的,CPU通过向内部外设的寄存器写入特定值来操作内部外设
寄存器特点:按位进行规划和使用、读写是32位整体进行操作的
寄存器的操作要求:操作寄存器时,要求改变某一特定位,而不影响其他位
如何实现:读出—>改相应位—>写入
(2)改相应位的操作
​ 基本思路:寄存器特定位改变而不影响其他位,构造合适的1和0组成的数和这个寄存器原来的值位与、位或、位取反

特定位清零用 & (用0清零)

与1位与无变化、与0位与变为0

要清零的位为0,其他位为1,然后这个数与原数位与

举例:原32位寄存器的值位0xAAAAAAAA,对bit2~bit8清零

​ 0xAAAAAAAA & 0xFFFFFE03

特定位置1用 | (用1置1)

与1位或为1,与0位或无变化

要置1的位为1,其他位为0,然后这个数与原数位或

举例:原32位寄存器的值位0xAAAAAAAA,对bit8~bit15置1

​ 0xAAAAAAAA | 0x0000FF00

特定位取反用^ (用1取反)

与1位异或为会取反,与0位异或无变化

要取反的位为1,其他位为0,然后这个数与原数位异或

举例:原32位寄存器的值位0xAAAAAAAA,对bit8~bit15取反

​ 0xAAAAAAAA ^ 0x0000FF00

代码举例

unsigned int a = 0xa12aaaa7;
unsigned int b = 0xFFFF00FF;
unsigned int c;
c = a & b;
// 16进制数的打印
printf(“a & b = %#X.\n”, c);
printf(“a & b = 0x%x.\n”, c);
3、使用位运算构建特殊二进制数
(1)使用位移获取特定位为1的二进制数
获取bit3-bit7为1,同时bit23-bit25为1,其余都为0的数
(0x1F<<3) | (0x7<<23)
(2)使用位移获取特定位为0的二进制数
获取bit4-bit10为0,其余都为1的数
bit4-bit10为0也就是bit0-bit3为1同时bit11-bit31为1
(0xF<<0) | (0x1FFFFF<<11)
~(0x7F<<4)
(3)总结
1少0多,连续多个1左移n位
0少1多,先构建其位反数,再按位取反
连续1(0)不止一个,则通过分段分别构建,再彼此位与即可
4、位运算的操作实战
​ 回顾:清零用&,置1用 | ,取反用^

​ ~和<< >>用来构建特定二进制数

unsigned int a ; // 定义无符号数,如果是有符号数,那么位移就会出问题

(1)给定一个整型数a,设置a的bit3,保证其他位不变
a |= (0x1<<3)
(2)给定一个整型数a,设置a的bit3-bit7,保证其他位不变
a |= (0x1F<<3) a |= (0b11111<<3)
(3)给定一个整型数a,清除a的bit15,保证其他位不变
a &= (~(0x1<<15)) a = (a | (0x7FFF<<0) | (a | (0xFFFF<<16) (不采用)
(4)给定一个整型数a,清除a的bit15-bit23,保证其他位不变
a &= (~(0x1FF<<15))
(5)给定一个整型数a,取出a的bit3-bit8
先将a的bit3-bit8不变,其余位清零
a &= (0x3F<<3)
再将a右移3位
a >>= 3
(6)给寄存器的bit7-bit17赋值937,其余位不受影响
先将bit7-bit17全部清零,其他位不变
a &= (~(0x7FF<<7))
再将937写入bit7-bit17,其他位不变
a |= (937<<7)
3、使用位运算构建特殊二进制数
(1)使用位移获取特定位为1的二进制数
获取bit3-bit7为1,同时bit23-bit25为1,其余都为0的数
(0x1F<<3) | (0x7<<23)
(2)使用位移获取特定位为0的二进制数
获取bit4-bit10为0,其余都为1的数
bit4-bit10为0也就是bit0-bit3为1同时bit11-bit31为1
(0xF<<0) | (0x1FFFFF<<11)
~(0x7F<<4)
(3)总结
1少0多,连续多个1左移n位
0少1多,先构建其位反数,再按位取反
连续1(0)不止一个,则通过分段分别构建,再彼此位与即可
4、位运算的操作实战
​ 回顾:清零用&,置1用 | ,取反用^

​ ~和<< >>用来构建特定二进制数

unsigned int a ; // 定义无符号数,如果是有符号数,那么位移就会出问题

(1)给定一个整型数a,设置a的bit3,保证其他位不变
a |= (0x1<<3)
(2)给定一个整型数a,设置a的bit3-bit7,保证其他位不变
a |= (0x1F<<3) a |= (0b11111<<3)
(3)给定一个整型数a,清除a的bit15,保证其他位不变
a &= (~(0x1<<15)) a = (a | (0x7FFF<<0) | (a | (0xFFFF<<16) (不采用)
(4)给定一个整型数a,清除a的bit15-bit23,保证其他位不变
a &= (~(0x1FF<<15))
(5)给定一个整型数a,取出a的bit3-bit8
先将a的bit3-bit8不变,其余位清零
a &= (0x3F<<3)
再将a右移3位
a >>= 3
(6)给寄存器的bit7-bit17赋值937,其余位不受影响
先将bit7-bit17全部清零,其他位不变
a &= (~(0x7FF<<7))
再将937写入bit7-bit17,其他位不变
a |= (937<<7)
(7)将寄存器bit7-bit17中的值加17,其余位不受影响
先读出bit7-bit17的值
temp = a & (0x7FF<<7)
temp >>= 7;
给temp加17
temp += 17
将bit7-bit17清零
a &= (~(0x7FF<<7))
将temp算出的值写入到bit7-bit17
a |= (temp<<7)
(8)给寄存器bit7-bit17赋值937,同时给bit21-bit25赋值17
先将bit7-bit17和bit21-bit25清零,其他位不变
a &= (~ ( (0x7ff<<7) | (0x1f<<21) ) ) // 位或优先级高于~
再将937赋值到bit7-bit17,17赋值到bit21-bit25
a |= ((937<<7) | (17<<21))
printf(“a = %u.\n”, a); // %u 打印无符号数

5、技术升级:使用宏定义来完成运算
(1)直接用宏定义来置位、复位(最右边是第一位,bit0)
// 置位
#define SET_NTH_BIT(x, n) (x | ( (1U) << (n-1) ) )
// 复位
#define CLEAR_NTH_BIT(x, n) (x & ~ ( (1U) << (n-1) ) )
// 1后边的U表示这个数字是无符号数(有符号数的右移是会出问题的)
1
2
3
4
5
(2)将32位数x的第n位到第m位置位
// 关键点:我们需要一个算式来得到(m-n+1)个1的十六进制数
// 第1步,先得到32个1: ~0U (~按位取反得到32个1,如果直接1U那么就只有bit0位1)
// 第2步,将得到的数右移x位即可得到(m-n+1)个: (~0U) >> (32-(m-n+1)) 或 (0U<<(m-n+1))
#define SET_BIT_N_M(x, n, m) (x | (((~0U) >> (32-(m-n+1))) << (n-1)))
#define SET_BIT_N_M(x, n, m) (x | (0U<<(m-n+1))<<(n-1))
1
2
3
4
5
(3)截取变量的部分连续位(相当于4中的(5))
// 给定一个整型数a,取出a的bit3-bit8
// 先将a的bit3-bit8不变,其余位清零 a &= (0x3F<<3)
// 再将a右移3位 a >>= 3
#define GETBITS(x, n, m) (x & (0U << (m-n+1)) << (n-1)) >> (n-1))
// 思路:
// 先将x的bit(n-1)-bit(m-1)不变,其余位清零
// 得到(m-n+1)个1的十六进制数 (0U << (m-n+1)) (得到0x3F)
// 将得到的16进制数左移(n-1) (0U << (m-n+1)) << (n-1) (得到0x3F<<3)
// x和左移后的数位与 x & (0U << (m-n+1)) << (n-1) (a &= (0x3F<<3))
// 再将x右移(n-1)位
// 位与后的数右移(n-1) (x & (0U << (m-n+1)) << (n-1)) >> (n-1)
// 最终结果
#define GETBITS(x, n, m) (x & (0U << (m-n+1)) << (n-1)) >> (n-1))
1
2
3
4
5
6
7
8
9
10
11
12
13
// 得到(m-n+1)个1的十六进制数的两种方式:
// 32位的1先左移(m-n+1)位,那么低(m-n+1)位位0,高(32-(m-n+1))位为1,再将其按位取反,
// 就得到低(m-n+1)位为1,高(32-(m-n+1))位为0。
(0U << (m-n+1))
// 有(m-n+1)个1,那么就有32-(m-n+1)个0,将32位的1先右移32-(m-n+1),
// 那么高32-(m-n+1)位为0,低(m-n+1)位为1。
(~0U) >> (32-(m-n+1))
————————————————
版权声明:本文为CSDN博主「叮咚咚€」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qixjocd12345/article/details/108304967

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值