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);
}