位运算的种类
在进行位运算的时候要把数据转换成二进制位,并且全部都是补码的形式。
前面文章讲过怎么将十进制二进制互转 https://mp.weixin.qq.com/s/o-ddNSI2GvFktbmtfZ5W7w
-
**按位与 & :**两个同为1的时候才为1,否则为0
3 & 4 = ?
首先将3转换成二进制数:11,补码形式:0b00000000000000000000000000000011(int占4个字节,一个字节=8位,共32位)
再将4转换成二进制数:100,补码形式:0b00000000000000000000000000000100(共32位)
那么 3&4 = 0b00000000000000000000000000000000 = 0
-
**按位或 | :**两个中只要有一个为1,结果就为1
3 | 4 = ?
首先将3转换成二进制数:11,补码形式:0b00000000000000000000000000000011(共32位)
再将4转换成二进制数:100,补码形式:0b00000000000000000000000000000100(共32位)
那么 3|4 = 0b00000000000000000000000000000111 = 7
-
**按位异或 ^:**两个不一样的时候才为1,一样则为0
3 ^ 4 = ?
首先将3转换成二进制数:11,补码形式:0b00000000000000000000000000000011(共32位)
再将4转换成二进制数:100,补码形式:0b00000000000000000000000000000100(共32位)
那么 3^4 = 0b00000000000000000000000000000111 = 7
-
**按位取反 ~ :**1变0,0变1
~3 = ?
首先将3转换成二进制数:11,补码形式:0b00000000000000000000000000000011(共32位)
取反后的结果:0b11111111111111111111111111111100(补码 负数)
补码:原码取反+1
通过补码求原码:补码 - 1 --> 取反(取反的过程中符号位不变,也就是b后面的一位)
0b11111111111111111111111111111100减1 = 0b11111111111111111111111111111011
0b11111111111111111111111111111011取反:0b10000000000000000000000000000100 = -4
-
**左移 << :**让操作数乘以2的n次幂 n就是移动的位数
3 << 2 = ?
3 << 2 = 3 ∗ 2 2 3*2^2 3∗22 = 12
那计算机底层是如何计算的呢?
3的二进制位:00000000000000000000000000000011,左移两位: 00000000000000000000000000001100 = 12
-
**右移 >> :**让操作数除以2的n次幂 n就是移动的位数
32 >> 2 = ?
32 >> 2 = 32 / 2 2 32 / 2^2 32/22 = 8
32的二进制位:00000000000000000000000000100000,右移两位:00000000000000000000000000001000 = 8(如果符号位是1,那么左边就用1补充,符号位是0就用0补充)
-32 >> 2 = ?
-32的二进制位:(位运算都是针对补码进行操作的)
原码:10000000000000000000000000100000
反码:11111111111111111111111111011111
补码:11111111111111111111111111100000(补码 = 反码 + 1)
-32向右移动2位:11111111111111111111111111111000 (补码)
根据补码求原码:(反码 = 补码 - 1)
11111111111111111111111111111000 - 1 = 11111111111111111111111111110111
原码:11111111111111111111111111110111取反 = 10000000000000000000000000001000 = -8
-
**无符号右移 >>> :**让操作数除以2的n次幂 n就是移动的位数
无符号右移与右移的区别:
有符号右移:右边的末位被挤掉,左边用符号位的数字补充
无符号右移:右边的末位被挤掉,左边用0补充
-32 >> 2 = 8
音频、视频…属于无符号数据。
应用
-
按位与 & 的应用
-
判断一个数是奇数还是偶数
if(n & 1 == 1){ // n 是个奇数。 }
1的二进制数是1,按位与的作用是两边都是1的时候才会返回1,假设n=6
那么6的二进制表示为:0b00000000000000000000000000000110
1的二进制表示为:0b00000000000000000000000000000001
n & 1 = 0,所以n是偶数。
也就是只判断n的最末一位,如果是1则是奇数,0是偶数。
-
-
异或 ^ 的应用
-
一个数据对相同的两个数据异或两次,其值不变。一般应用在加密上。
a ^ b ^ b = a
其中b可以用作秘钥,客户端对a进行异或操作后传给服务端,服务端把这个值再进行异或后得到原始值。
-
实现两个值的交换
int x = 10; int y = 20; x = x ^ y; y = x ^ y; // 变量替换 y = (x ^ y) ^ y (前面讲过一个数据对相同的两个数据异或两次值不变,那么y = x) x = x ^ y; // 变量替换 x = x ^ (x ^ y) 那么x = y
-
面试
-
int型变量在内存中是如何存储的?
因为int占4个字节,每个字节占二进制的8位,一共对应二进制的32位,如十进制的数字7,对应二进制应该是:
0b00000000000000000000000000000111(0b表示二进制数)
-
int的取值范围是如何确定的?
如十进制的数字7,对应二进制应该是:0b00000000000000000000000000000111,最左边的第一位(b后面的第一位)表示符号位(0正数 1负数),从第二位开始直到最后一位表示数值位,所以int的取值为-2的31次幂 到 2的31次幂
-
为什么int的取值范围不是-2的32次幂 到 2的32次幂?
因为int的二进制位一共是32位,其中第一位表示符号位,其余的31位才表示数值位。
-
如果0b00000000000000000000000000000111表示十进制的数字7,那0b10000000000000000000000000000111表示为十进制的数字-7吗?为什么?
不是。计算机中存储有符号数的时候是按照补码的形式存进去的。
0b10000000000000000000000000000111不是二进制的补码,而是二进制的源码。
计算机中所有的有符号数据都是按照补码形式存储的。
正数的原码、反码、补码都一样。
负数的原码、反码、补码不一样。
-
什么是原码、反码、补码?
原码:符号位+数值位
反码:除符号位不变,其他位(数值位)全部1变0,0变1
补码:在反码的基础上加1
-
7 和 -7 的原码、反码、补码分别怎么表示?
-
7的原码、反码、补码
原码:0b10000000000000000000000000000111
反码:0b10000000000000000000000000000111
补码:0b10000000000000000000000000000111
-
-7的原码、反码、补码
原码: 0b10000000000000000000000000000111
反码:0b11111111111111111111111111111000
补码:0b11111111111111111111111111111001
-
-
0b11111111111111111111111111111001的原码是什么?
根据补码求原码:
- 根据补码减1得到反码
- 根据反码取反得到原码
反码 = 补码0b11111111111111111111111111111001 - 1
= 0b11111111111111111111111111111000;
原码 = 反码取反0b11111111111111111111111111111000
= 0b10000000000000000000000000000111;