文章目录
- 一、位操作符号
-
- 1.1 位与 &
-
- 1.1.1 逻辑与和位与的区别
- 1.2 位或 |
- 1.3 位取反 ~
-
- 1.3.1 规律
- 1.4 位异或 ^
- 1.5 左右位移
- 二、位与位操作
-
- 2.1 寄存器操作的要求
- 2.2 特定位置清零
- 2.3 特定位置置1
- 三、运用位运算构建特定的二进制数
-
- 3.1为啥要构建二进制数
- 3.2 如何用位移来构建位为1的二进制数
- 3.3 如何用位移来构建位为0的二进制数
- 四、练习编程
-
- 4.1 给定一个整形数a,设置bit3,保证其余位置不变。
- 4.2 给定一个整形数a,设置bit3-bit7,保证其余位置不变。
- 4.3 给定一个整形数a,清除bit5,保证其余位置不变。
- 4.4 给定一个整形数a,清除bit15-bit23,保证其余位置不变。
- 4.5 给定一个整形数a,取出bit3-bit8.
- 4.6 给一个寄存器的bit7 - bit17赋值937(其余位不受影响)
- 4.7 给一个寄存器的bit7 - bit17中的值加上17(其余位不受影响)
- 给一个寄存器的bit7 - bit17赋值937,bit23 - bit 27赋值9;
一、位操作符号
1.1 位与 &
位与操作符号是一个 &、两个 && 是逻辑与。
真值表:1 & 0 = 0; 1 & 1= 1; 0 & 0 = 0; 0 & 1 = 0;
只有两个都是1的情况下才能是1,其余任何情况下都是0;
1.1.1 逻辑与和位与的区别
位与:两个二进制数按照位置一一对应进行计算。(0XAA & 0XF0 = 0XA0;)
逻辑与:两个二进制数作为整体进行计算。(0XAA && 0XF0 = 1)
1.2 位或 |
位或操作符号是一个 |、两个 || 是逻辑或。
真值表:1 | 0 = 1; 1 | 1= 1; 0 | 0 = 0; 0 | 1 = 11;
只有两个都是0的情况下才是0,其余都是1;
逻辑或和逻辑与道理一样,做为整体来取或;
1.3 位取反 ~
位取反操作符号是一个 ~ ,一个 ! 是逻辑取反。
位取反就是把 二进制数从0变成1,从1变成0;
逻辑取反就是把不是0得到数变成0;把爱0变成1;
下面看代码:
#include <stdio.h>
int main()
{
unsigned int a = 45,b = 0,c,d,e,f;
c = ~a;
d = !a;
e = ~b;
f = !b;
printf("c = %u\n",c);
printf("d = %u\n",d);
printf("e = %u\n",e);
printf("f = %u\n",f);
return 0;
}
运行结果:
c = 4294967250
d = 0
e = 4294967295
f =1
为啥位取反会变成这么大一个数呢,
因为45的二进制是:0010 1101;
那么取反就算是 1101 0010;而编译器是32的话就是有32个二进制数,所以之前的所有0就会变成1;
这里面的所有1会变成0,0会变成1。
1.3.1 规律
任何非0的数按逻辑取反后再取反就会得到1;
任何非0的数按位取反后再取反就会得到他本身;
#include <stdio.h>
int main()
{
unsigned int a = 45,c,d;
c = ~~a;
d = !!a;
printf("c = %u\n",c);
printf("d = %u\n",d);
return 0;
}
c = 45
d = 1
1.4 位异或 ^
这个符号比较少用。
真值表: 1 ^ 1 = 0; 0 ^ 0 =0;1 ^ 0 = 1; 0 ^ 1 = 1;
两个数相同就得到0,不相同就得到1;
1.5 左右位移
对于无符号数来说:
符号 : 左位移: <<
右位移: >>
原来的二进制
左移一位后的二进制
简单理解来说,左移就是整体往左边移动。
最左边的那个0呢?移走了,先不管他。最右边需需要补上一个0。
右位移与之相反
左移一位后的二进制
简单理解来说,右移就是整体往右边移动。
最右1边的那个1呢?移走了,先不管他。最左边需需要补上一个0。
对于有符号的来说:
正数移动补0;负数移动补1;这个叫算术移位;
二、位与位操作
我这里主要是用来操作寄存器的,其余操作不做解释。采用无符号的数据
2.1 寄存器操作的要求
指定寄存器的特定端口进行操作,而不影响其他位置。
怎样去操作寄存器:
为了不影响其余的寄存器,我们会把想要修改的寄存器地址先读出来,然后就修改他的特定值,再将修改后的值整体写入寄存器中,这样就不会影响其余的值。
2.2 特定位置清零
用与符号 &
因为只有两个都是1的时候才会变成1.其余都是0;任何数与1与都是本身,和 0 与都是0;
举个例子:
和
进行与运算,得到的结果是
2.3 特定位置置1
用符号 |
因为1 和 什么或都是1,和0或的时候都是本身。
和
进行或运算,那么运算结果就是
三、运用位运算构建特定的二进制数
3.1为啥要构建二进制数
为了后期维护程序。比如运用工具(计算机、二进制转化器)可以直接让一个10进制转换化为二级制数,这样确实很方便呢,但是过了一段时间过后,我们已经记不住这个数是代表啥了。所以我们需要自己去写代码次才能让我们每次写代码的时候,明白这串二进制数的原理。
3.2 如何用位移来构建位为1的二进制数
举个例子来说
我写一个
0XF8
你一定知道这是一个16进制的数字,但是二进制时候多少呢,你不知道啊,你还得去算算。诚然现在有计算器可以帮你,但是假设你没有计算器呢。你得去用手算(我是转行的,进制转化这是一个薄弱点)我觉得麻烦,所以我的想用代码来实现一下,可以简单明了。
这里可以看出,我的bit3-bit7是1,其余都是0,那么是不是五个额连续的 1 左移了3位。
四个连续的1是不是就是 F 写成16进制就是 0XF 左移三位时候不是就是 (0XF << 3)
那我再复杂一点:bit24–bit2是1 bit4-bit7是1 这怎么办呢
bit24-bit27是4个1 bit4-bit7是4个1,那么我们也可以通过偏移的方式来写 (0XF << 4 | 0XF << 24)
让我们来检验一下:
#include <stdio.h>
int main()
{
unsigned int a = 0XF;
unsigned int b;
b = (( a << 4) | (a << 24));
printf("b = 0x%x.\n",b);
return 0;
}
结果
是不是答案是对的,虽然我们同样也需要计算一点,但是没有那么大,只需要计算一小部分就好了。
3.3 如何用位移来构建位为0的二进制数
同样的道理,假设我们 bit4-bit7 为0,其余部分为1,我们可以先把0看成1,把1看成0;然后取反
#include <stdio.h>
int main()
{
unsigned int a = 0XF;
unsigned int b;
b = ~(a << 4);
printf("b = 0x%x.\n",b);
return 0;
}
如果其他位也有0的话可以 用或 连起来就行了。
四、练习编程
4.1 给定一个整形数a,设置bit3,保证其余位置不变。
unsigned int a;
a = a | (1 << 3);
4.2 给定一个整形数a,设置bit3-bit7,保证其余位置不变。
unsigned int a;
a = a | (0x1f << 3);
4.3 给定一个整形数a,清除bit5,保证其余位置不变。
unsigned int a;
a = a & (~(1 << 5));
4.4 给定一个整形数a,清除bit15-bit23,保证其余位置不变。
unsigned int a;
a = a & (~(0X1FF << 15));
4.5 给定一个整形数a,取出bit3-bit8.
unsigned int a = 0XC30288F8; //随机定义一个树
a &= (0x3 << 3); //保留bit3 -bit 8,其余全部置零
a = a>> 3; //把保留的bit3-bit8移过去
4.6 给一个寄存器的bit7 - bit17赋值937(其余位不受影响)
unsigned int a = 0XC30288F8; //随机定义一个树
a &= ~(0x3FF << 7); //保留bit7 - bit17,其余全部置零
a |= (937 << 7); //把赋值的数移动到bit7 - bit17
4.7 给一个寄存器的bit7 - bit17中的值加上17(其余位不受影响)
unsigned int a = 0XC30288F8; //随机定义一个树
unsigned int b; //定义一个数用来存放值
b = a & (0x3FF << 7) ; //保留bit7 -bit17,其余全部置零
b = b >> 7; //读出原来 bit7 - bit17里面的数
b += 17 << 7; //+17
a &= (~(0x3FF << 7)); //将寄存器里面的bit7 -bit17置零
a |= b; //将b里面的数放进去
给一个寄存器的bit7 - bit17赋值937,bit23 - bit 27赋值9;
unsigned int a = 0XC30288F8; //随机定义一个数
a &= ~((0X3FF << 7) | (0X1F << 23)); //清零
a |= (937 << 7) | (9 << 23); //赋值