为什么用16进制
计算机硬件是0101
二进制的,16进制刚好是2的倍数,更容易表达一个命令或者数据。
最早规定ASCII字符集采用的就是8bit
(后期扩展了,但是基础单位还是8bit), 8bit
用2个16进制直接就能表达出来,不管阅读还是存储都比其他进制要方便。
计算机中CPU运算也是遵照ASCII字符集,以16、32、64的这样的方式在发展,因此数据交换的时候16进制也显得更好。
为了统一规范,CPU、内存、硬盘我们看到都是采用的16进制计算。
目前16进制的一些用途
网络编程,数据交换的时候需要对字节进行解析都是一个byte一个byte的处理,1个byte
可以用0xFF
两个16进制来表达。
数据存储,存储到硬件中是0101
的方式,存储到系统中的表达方式都是byte
方式。
一些常用值的定义,比如:我们经常用到的html中color表达,就是用的16进制方式,4个16进制位可以表达好几百万的颜色。
&0xFF的作用
首先我们要都知道, &
表示按位与
,只有两个位同时为1
,才能得到1
,0x
代表16进制数,0xff
表示的数二进制1111 1111
占一个字节。和其进行&
操作的数,最低8位,不会发生变化。
- 取得低八位
通常配合==
移位操作符>>==
使用。
例如:java socket通信中基于长度的成帧方法中,如果发送的信息长度小于65535
字节,长度信息的字节定义为两个字节长度。这时候将两个字节长的长度信息,以Big-Endian的方式写到内存中
out.write((message.length>>8)&0xff);//右移8位,取高八位写入地址
out.write(message.length&0xff);//取低八位写入高地址中
例如,有个数字 0x1234
,如果只想将低8位写入到内存中: 0x1234&0xff
- 0x1234 表示为二进制 0001 0010 0011 0100
- 0xff表示为二进制 1111 1111
两个数做与操作,显然将0xff
补充到16位,就是高位补0
。此时0xff
为 0000 0000 1111 1111
。
与操作 1&0 =0
、1&1 =1
这样 0x1234
只能保留低八位的数 0000 0000 0011 0100
也就是 0x34
- 保证补码的一致性
我们只关心二进制的机器数而不关注十进制的值,那么byte
&0xff
只是对其最低8位的复制,通常配合逻辑或 |
使用,达到字节的拼接,但不保证其十进制真值不变。
public static void main(String[] args) {
byte b = -127;//10000001
int a = b;
System.out.println(a);
a = b&0xff;
System.out.println(a);
} //输出结果-127,129
using System;
using System.Text;
class Program
{
static void Main(string[] args)
{
sbyte b = -127;
int a = b;
Console.WriteLine(a);
a = b & 0xff;
Console.WriteLine(a);
}
}
执行结果:
-127
129
乍一看,b
是8位的二进制数,在与
上0xff
(也就是 1111 1111
),不就是其本身吗,输出在控制台结果为什么是129
呢?
首先计算机内的存储都是按照补码存储的(如果不清楚补码可以去看这篇 原码、反码、补码),-127
补码表示为 1000 0001
int a = b;
将 byte
类型提升为 int
时候,b
的补码提升为 32位,补码的高位补1
,也就是:
1111 1111 1111 1111 1111 1111 1000 0001
负数的补码转为原码,符号位不变,其他位取反:
1000 0000 0000 0000 0000 0000 0111 1110
再加1
,正数的补码,反码都是本身。结果是
1000 0000 0000 0000 0000 0000 0111 1111
表示为十进制 也是 -127
。
也就是 byte
-> int
能保证十进制数不变,但是有些时候比如文件流转为byte
数组时候,
我们不是关心的是十进制数有没有变,而是补码有没有变,这时候需要&
上0xff
本例子中,将byte
转为int
,高的24位必将补1
,此时补码显然发生变化,
1111 1111 1111 1111 1111 1111 1000 0001
再&上0xff
,将高的24重新置0
,0xff
:
0000 0000 0000 0000 0000 0000 1111 1111
结果是
0000 0000 0000 0000 0000 0000 1000 0001
这样能保证补码的一致性,当然由于符号位发生变化,表示的十进制数就会变了
和原来的补码 一致,但是显然符号位变化了,表示的十进制数发生变化,变为129
总结
基本类型从小扩展到大的数据类型时候,正数因为符号位是0
,无论如何都是补零扩展,但是负数补零扩展和补符号位1
扩展完全不同,因此负数补符号位1
扩展,保证十进制数不变
例如 byte
>>>int
-127
自动按照补符号位1
扩展,在高24位补符号位1
,表示的十进制数不变
补零扩展,保证补码的一致性,但是表示的十进制发生变化。