一、进制
首先,我们应该知道在计算机内部,所有的信息最终都表⽰为⼀个⼆进制的字符串,每一个二进制位(bit)不是0就是1(低电平和高电平)。因此了解进制和进制之间的转换对我们来说非常重要。常见的进制有:二进制(BIN/Binary)、八进制(OCT/Octonary)、十进制(DEC/Decimal)、十六进制(HEX/Hexadecimal)。
1.不同的进制
二进制,顾名思义满二进一,由比2小的数字(0和1)组成,从右往左分别表示2的0次幂,2的1次幂,2的2次幂...,例如:
二进制数:1010
分别表示:8421
在对应位数上有1,表示有一个值;0则表示没有,
即该二进制数的值(转换为十进制)是:1*8+0*4+1*2+0*1 = 10
十进制也是我们最常用的进制,满十进一,所以组成数字为小于十的数字,从右往左分别从10的0次幂,10的1次幂...依次对指数递增。
八进制也是我们最常用的进制,满八进一,所以组成数字为小于八的数字,从右往左分别从8的0次幂,8的1次幂...依次对指数递增。例如:
八进制数:4615
表示:8^3 8^2 8^1 8^0
该八进制数的值(十进制)为:2445
十六进制,满十六进一,所以组成数字为小于十六的数字,0~9,A代表10,B代表11,C代表12,D代表13,E代表14,F代表15,从右往左分别从16的0次幂,16的1次幂...依次对指数递增。
十六进制数:7B
表示: 16^1 16^0
该十六进制数的值(十进制)为:123
2.进制间的相互转换
1)十进制转二进制、八进制、十六进制
例如下图:将173从十进制表示转换到二进制表示
上面就是十进制数转二进制的方法,同理十进制数转八进制表示就是对8取余,然后逆序排列;十进制转十六进制就是对16取余,然后逆序排列。
2)二进制转十进制,八进制,十六进制
二进制转十进制
例如:1010 ,每一位从右往左代表2的0次幂,2的1次幂,2的2次幂,2的3次幂,
然后有1乘1,有0归0
所以就等于1*2^3+1*2^1 = 10
二进制转八进制
例如:111011,每三位看做一个八进制数
所以111 依次从右往左2的0次幂,2的1次幂,2的2次幂,代表就是 7
所以011 依次从右往左2的0次幂,2的1次幂,2的2次幂,代表就是 3
111011的八进制表示就是73
二进制转十六进制
例如:11011011,每四位看做一个十六进制数
所以1101 依次从右往左2的0次幂,2的1次幂,2的2次幂,2的3次幂,代表就是 D
所以1011 依次从右往左2的0次幂,2的1次幂,2的2次幂,2的3次幂,代表就是 B
11011011的十六进制表示就是DB
掌握以上的进制转换基本就够用了,接下来介绍原码、反码、补码。
二、原码,反码和补码
1.机器数
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1。例如:
十进制 二进制
+3 0000 0011
-3 1000 0011
注:以上举例以计算机字长为八列举。计算机字长是处理器处理数据的宽度。是CPU一次能够处理的二进制的数据位数,字长越长,计算机的运算速度精度就越高。我们常见的32位、64位(操作系统),就是计算机的字长。以下的例子也以字长为八举例。
2.真值
因为以上的二进制第一位是符号位,所以机器数的形式值就不等于真正的数值。即上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(十进制表示)。因此将带符号位的机器数对应的真正数值称为机器数的真值。
3.原码
原码就是符号位,加上除去符号位剩余二进制数的真值。例如:
+3的原码:0000 0011
-3的原码:1000 0011
4.反码
正数的反码就是自己的原码,负数的反码就是其原码符号位不变,其余各位取反(0取反是1,1取反是0),例如:
+3的反码:0000 0011
-3的反码:1111 1100(-3的原码:1000 0011)
5.补码
正数的补码还是自己的原码,负数的补码就是它的反码+1,例如:
+3的补码:0000 0011
-3的补码:1111 1101(-3的原码:1000 0011; -3的反码:1111 1100)
注:计算机在底层存储数据的时候,一律存储的是“二进制的补码形式"。
6.为什么要使用原码,反码,补码
原码, 反码, 补码是计算机内部存储一个具体数字的编码方式。根据以上的例子,我们可以知道,正数的原码,反码,补码都是一样的,区别在于负数,而补码可以表示的范围是最广的。具体原因可以看下面这篇文章,写的超级详细:https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
三、字符集编码
1.ASCII
由上面可以得知,计算机只能识别和存储二进制数,数字可以通过补码来实现存储,那么如何让计算机存储非数字的字符数据呢?
美国国家标准学会(American National Standard Institute , ANSI )制定了一种标准的单字节字符编码方案,用于基于文本的数据的编码,ASCII (American Standard Code for Information Interchange——美国信息交换标准代码)
2.Unicode
很显然,对于英语和其他一些西欧语言来说,足够了,英文字母总共才几个对吧,那么对于汉字呢?ASCII显然是不够用的,所以出现了GBK等以支持汉字,那么,日语、韩语呢?于是,人们意识到他们应该提出一种标准方案来展示世界上所有语言中的所有字符,出于这个目的,Unicode诞生了。
Unicode 是一种字符集,规定了符号对应的二进制代码,注意Unicode只是一个符号集,具体的实现是UTF-8(常用)、UTF-16、UTF-32,java源代码就是采用Unicode编码方式。Unicode最初采用16个二进制位来表示符号的码值,也就是最多支持 (2的16次幂) 65536个字符,也就是0 ~ 65535,对应Unicode码值(十六进制表示)范围是0x0000 ~0xFFFF,Unicode使用“U+前缀”也就是U+0000 ~ U+FFFF 。
随着更多字符的增加,65536 是不够用的,于是Unicode 不得不进行扩展,于是使用8位用作扩展位,形式如下
一个字节8位可以表示 2的8次幂 = 256 个数,最大可以扩展为 256 *65526=16777216 个,不过也不需要那么多的字符,仅仅使用了前面的17个,也就是
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
扩展后的范围为U+000000 ~ U+10FFFF 。
Unicode 没有规定字符对应的二进制码在计算机中如何存储,只是规定了他的值是多少而已
一个字符对应的实际的值,我们称之为代码点 code point。
那么一个码点实际的值怎么存储呢?以下以UTF-8举例说明
3.UTF-8
UTF-8 是目前互联网上使用最广泛的一种 Unicode 编码方式,可变长使用 1 - 4 个字节表示一个字符,根据字符的不同变换长度。
UTF-8的编码规则很简单,只有⼆条:
① 对于单字节的符号,字节的第⼀位设为0,后面7位为这个符合的Unicode码值。因此对于英文字母,UTF-8和ASCII是相同的。
② 对于n字节的符合(n>1),第一个字节的前n位都设置为1,第n+1位设置为0,后面字节的前两位一律设置为10。剩下没有占用的二进制位就是Unicode码值。
Unicode对应码值(十六进制) | 使用的字节数 | UTF-8编码方式(二进制) |
---|---|---|
0000 0000-0000 007F | 使用一个字节 | 0XXXXXXX |
0000 0080-0000 07FF | 使用两个字节 | 110XXXXX 10XXXXXX |
0000 0800-0000 FFFF | 使用三个字节 | 1110XXXX 10XXXXXX 10XXXXXX |
0001 0000-0010 FFFF | 使用四个字节 | 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX |
UTF-8编码的最大长度是4个字节,也就是最多有21个x 表示Unicode的最大码位0x10FFFF
0001 0000 1111 1111 1111 1111也只有21位。
例如:“严” 的Unicode码值是4E25(0100 1110 0010 0101),根据上表在0000 0800-0000 FFFF范围内,所有“严”需要三个字节表示,要使用的格式位1110XXXX 10XXXXXX 10XXXXXX,将X替换为Unicode码值,即”严”的UTF-8编码为:11100100 10111000 10100101,就是E4 B8 A5 。