一道面试题引发的“位运算符”

最近遇到一个比较有趣的面试题:创建一个方法实现两个int进行交换,并要求在方法中不能定义新的变量。
乍一看小菜一碟:

private void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        System.out.println("a = " + a + ",b = " + b);
}

咳咳咳,请注意审题!要求在方法中不能定义新的变量,上面的代码新增了局部变量temp,麻烦你在想一想🤔️。
嗯嗯,简略思考了下,那也简单:

private void swap(int a, int b) {
        a = a + b;
        b = a - b;
        a = a - b;
        System.out.println("a = " + a + ",b = " + b);
}

嗯,这样就能实现int a和b的值进行交换了,也没有新增变量。咳咳咳,int 是有范围区间的,如果a 跟 b的值特别大,超出 int 的值会损失精度,有没有更好的方案呢?额,的确会有这种情况,再容我三思:

private void swap(int a, int b) {
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println("a = " + a + ",b = " + b);
}

搞定,这就要用到位运算符中的异或(^),下面进入正题。

现代计算机中所有的数据都是以二进制的形式存储在设备中,即 0、1 两种状态。计算机对二进制数据进行的运算都是叫位运算。常用的位运算符有几种,与(&)或(|)异或(^)非(~)左移(<<)有符号右移(>>)无符号右移(>>>)

与(&)
运算规则:两个位都为1时,结果才为1。
例如:3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值是1。

0&0=0  0&1=0  1&0=0  1&1=1

与(&)的常用场景:

  • 清零
    将想要清零的二进制跟0相与(&),结果为零
  • 判断奇偶
    只要根据二进制最末位是0还是1来决定,0就是偶数,1就是奇数。因此可以用二进制跟1相与(&)。
  • 取一个数的指定位
    比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

或(|)
运算规则:参加运算的两个位只要有一个为1,其值为1。
例如:3|5即 0000 0011| 0000 0101 = 0000 0111,因此 3|5 的值得7。

0|0=0  0|1=1  1|0=1  1|1=1

或(|)的常用场景:

  • 对一个数据的某些位设置为1
    比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

异或(^)
运算规则:参加运算的两个位,相应位相同为0,相异为1。
例如:3^5即 0000 0011 ^ 0000 0101 = 0000 0110,因此 3|5 的值得6。

0^0=0  0^1=1  1^0=1  1^1=0

异或(^)的常用场景:

  • 交换两个int
  • 与0相异或值不变
    例如:1010 1110 ^ 0000 0000 = 1010 1110
  • 翻转指定位
    比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

非(~)
运算规则:对一个二进制数按位取反,即将0变1,1变0。
例如:~3 即~0000 0011= 1111 1100,因此 ~3 的值得-4(负4)。

~1=0  ~0=1

非(~)的常用场景:

  • 使一个数的最低位为零
    使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为"~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高

左移(<<)
运算规则:将一个数的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
例如:a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

有符号右移(>>)
运算规则:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

无符号右移(>>>)
运算规则:将一个数的各二进制位全部右移若干位,向右被移出的位被丢弃,左侧用0填充。因为符号位变成了 0,所以结果总是非负的。
对于非负数,有符号右移和无符号右移总是返回相同的结果。例如:11 >> 2 == 11 >>> 2。

OK,位运算符说完了,最后扯几句二进制的问题。

原码
原码是指一个二进制数左边加上符号位后所得到的码,且当二进制数大于0时,符号位为0;二进制数小于0时,符号位为1;二进制数等于0时,符号位可以为0(+0)或1(-0)。

反码
反码是带有符号位的二进制数表示;负数的反码是将其对应正数按位取反,正数和0的反码就是该数字本身。

补码
补码也是带有符号位的二进制表示;负数的补码是将其对应正数按位取反再加1,正数和0的补码就是该数字本身。

在计算机中的数是用补码来表示和存储的。

[+1] = [0000 0001]= [0000 0001]= [0000 0001]--------------------
[-1] = [1000 0001]= [1111 1110]= [1111 1111]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值