关于原码反码和补码以及byte&0xff
1. 问题由来
本笔记是由byte&0xff引申出来的。在看某一段代码的时候发现有这么一个逻辑:
该方法的功能是把四个元素的byte数组转换成ip4地址,从debug的中间过程可以看出来src的第二个元素为负数-100,但它确实是表示ip地址的第二个字节,且src[1] & 0xff之后又变为了正数156,这其中的现象如何解释?要想知道这里面的原因,首先需要知道原码、反码和补码的概念。
2. 原码、反码和补码
数字是可以二进制来表示的,比如8的二进制表示为00001000,-8的二进制为10001000,其中最高位为符号位。对于正数来说,其二进制原码,反码,补码均相同。而对于负数来说,反码等于符号位位不变,其余各位取反;补码等于其反码加1。
比如问题中的156,其二进制表示为10011100,其反码和补码也是10011100。而-100的二进制表示为11100100,其反码为10011011,补码为1011100。这时候会发现156的原码和-100的补码是一致的。
3. byte & 0xff的细节
我们知道byte是java的一种基本类型,其大小为一个字节,表示的整数范围是-128~127。而当其最高为不解释为符号位时,其最大可以表示的数为255。因此例子中字节数组的第二个元素debug时被解释为-100的原因是,156的二进制表示正好可以解释为带符号byte的-100,且我们可以发现byte中存储的内容其实是补码。
ok,第一个问题清楚了,-100的补码与无符号的156的二进制表示一致,且java中的byte存储的是补码。那么,为什么src[1] & 0xff之后就变回了正数呢?讲道理,0xff可以表示为11111111,我们知道和1与运算之后还是其本身,乍一看,这个运算是没有意义的。
0xff究竟是怎么一回事呢。可以考虑一下下面这段代码中c的值会是什么:
int a = 255; int b = 0xff; boolean c = a == b;
c的值为true。其实0xff与255没有本质的区别,一个是十进制一个是十六进制,都是用来表示一个int型正数。回道上面的问题,src[1] & 0xff也就是src[1]与一个整数相与,那么src[1]首先就要先转换成一个四字节的整数:
11111111 11111111 11111111 10011100,而0xff为一个整数,其四字节表示为00000000 00000000 00000000 1111111,两者相与的结果为00000000 00000000 00000000 10011100,也即是解释为整数156,这样也就达到了想要获取byte中实际装入的无符号156的目的。