概述
右移运算
概念
右移运算符是将一个二进制数按指定移动的位数向右移动。
移动过程中,正数最高位补0,负数最高位补1,无符号数最高位补0。
补码
在计算机系统中,数值一律用补码来表示和存储,其中最高位表示符号位,1表示负数,0表示正数。
- 正数的补码是原码自身。
- 负数补码是通过原码计算得到,计算过程为:符号位不变,其余位按照原码取反加1
补码计算示例
以计算十进制-100的补码为例,计算过程为:
-100的原码:10000000 00000000 00000000 01100100
符号位保持不变,取反:11111111 11111111 11111111 10011011
加1后,-100补码为:11111111 11111111 11111111 10011100
右移运算
下面右移都是以整数为例,不考虑小数情况。
正数右移
正数右移高位需补0,以100右移4位为例:
操作 二进制 对应十进制
补码 00000000 00000000 00000000 01100100 100
右移4位 00000000 00000000 00000000 00000110 6
源码 00000000 00000000 00000000 00000110 6
最后可得: 100 >> 4 = 6
正数的右移相当于除法,右移几位就除以2的几次方,如100>>4 等效 100/2^4
负数右移
负数右移高位需补1,以-100右移4位为例:
操作 二进制 对应十进制
原码 10000000 00000000 00000000 01100100 -100
转换为补码 11111111 11111111 11111111 10011100 -100
右移4位,高位补1 11111111 11111111 11111111 11111001
保留符号位,按位取反 10000000 00000000 00000000 00000110
加1后转为源码 10000000 00000000 00000000 00000111 -7
最后可得: -100 >> 4 = -7
负数的右移不等于除法,即负数右移不能按除以2的n次方计算(n表示移动位数)。
无符号右移
无符号右移和正数右移相同,都是高位补0,以-100右移4位为例:
操作 二进制 对应十进制
原码 10000000 00000000 00000000 01100100 -100
转换为补码 11111111 11111111 11111111 10011100 -100
右移4位,高位补0 00001111 11111111 11111111 11111001
转为原码 00001111 11111111 11111111 11111001 268435449
最后可得: -100 >>> 4 = 268435449
右移28位后,最高位为0表示正数,所以原码即为补码本身。
“>>>”是Java里的无符号右移操作符。
总结
- 正数的右移,负数的无符号右移,就是相应的补码移位所得,在高位补0即可。
- 负数的右移,就是补码高位补1,然后按位取反加1即可。
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)
- bit4-bit10为0也就是bit0-bit3为1同时bit11-bit31为
(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表示这个数字是无符号数(有符号数的右移是会出问题的)
(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))
(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))
// 得到(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))
下面的不要看了,哈哈哈。
**
操作符与运算符:
1、基本操作符:“非”(¬)、“与”(^)、“或”(v)、“条件”(<->)、“双条件”(<->)。
“非”是一个一元操作符,只操作一项(¬p)。剩下是一元操作符,操作两项组成复杂语句(P^Q,PvQ,P->Q,P<->Q);
“与”(^)——>交集(∩);
“或”(v)——>并集(∪)。
2、逻辑运算符:与(&&)、或(||)、非(!)。
区别:
1、&&和||
&&:有0为0,同1出1。
||:
与:
或:
异或:
最后
以上就是曾经小霸王为你收集整理的寄存器的位操作和右移运算的全部内容,希望文章能够帮你解决寄存器的位操作和右移运算所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复