赞
踩
对于二进制所表示的数字来说,主要有三种,即无符号、补码以及浮点数。计算机的表示法是用有限数量的位来对一个数字编码,因此,当结果太大以至不能表示时,某些运算就会溢出。溢出会导致某些令人吃惊的后果。例如,int类型使用四个字节(32位二进制)来表示,计算表达式200300400*500会得出-884901888,这违背了数学运算特性。
大多数计算机使用8位的字节,作为最小的可寻址的内存单位,而不是访问内存中单独的位。换句话说,我们在访问内存的内容时,最小的访问单位是字节。机器级程序将内存抽象为一个非常大的字节数组,称为虚拟内存。内存的每个字节都由一个唯一的数字来标识,称为它的地址,数组下标是地址的增量,所有可能地址的集合就称为虚拟地址空间。虚拟地址空间是为了给机器级的程序一个概念上的映像,实际的实现是将动态随机访问存储器(DRAM)、闪存、磁盘存储器、特殊硬件和操作系统软件结合起来,为程序提供一个看上去统一的字节数组。
2.1.1 十六进制表示法
对于机器来说,可能比较喜欢0和1,但是对于人类这种高级生物来说,1和0就有点不够看了。因此通常情况下,为了便于阅读,我们会使用十六进制去表示二进制。1位十六进制的数字可以表示4位二进制数字,因此一个字节就可以表示为0x00—0xFF
1)十进制转十六进制:用十进制数不断的除以16,得到一个商和一个余数,余数用十六进制数表示作为最低位数。以此类推。例如:十进制数314156.
314156 ÷ 16 = 19634・・・・12 (C)
19634 ÷ 16 = 1227 ・・・・ 2 (2)
1227 ÷ 16 = 76 ・・・・11 (B)
76 ÷ 16 = 4 ・・・・12 (C)
4 ÷ 16 = 0 ・・・・ 4 (4)
所以十六进制数为0x4CB2C。
2)十六进制转十进制:用16的幂乘以每个十六进制数。例如:0x7AF.
7 x 16^2 + 10 x 16^1 + 15 x 16^0 = 1792 + 160 + 15 = 1976.
所以十进制数为1976
2.1.2 寻址和字节顺序
对于跨越多字节的程序对象,我们必须建立两个规则才能唯一确定一个程序对象的值:
1)这个对象的起始虚拟内存地址是多少?
2)以及在内存中此对象字节的排列顺序是什么(比如int类型4个字节的排列顺序是什么)?
对于第一个问题,由于大部分计算机都采用连续的内存地址去存储一个程序对象,因此我们称内存地址中最小的那个就是该程序对象的起始地址,也是该程序对象的地址。例如,假设一个类型为int的变量x的地址为0x100,也就是说,地址表达式&x的值为0x100,那么,(假设数据类型int为32位表示)x的4个字节将被存储在内存的0x100、0x101、0x102和0x103位置。
对于第二个问题,一般有两种方式,即大端法和小端法。
那么Java中如何确定当前环境所用的CPU是何种类型的字节顺序呢?
在java.nio.Bits中是这样实现的:
static { long a = unsafe.allocateMemory(8); try { unsafe.putLong(a, 0x0102030405060708L); byte b = unsafe.getByte(a); switch (b) { case 0x01: byteOrder = ByteOrder.BIG_ENDIAN; break; case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN; break; default: assert false; byteOrder = null; } } finally { unsafe.freeMemory(a); } }
原理很简单,就是先分配8个字节Long类型的内存,然后放内存中放入16进制0x0102030405060708L的数据,判断第一个字节是0x01还是0x08,如果是0x01,则说明大端字节顺序,如果是最大的0x08,则是小端字节顺序。
2.1.3 强制类型转转换
对于一个特定的数据类型来讲,计算机在解释这类数据的值的时候,是根据起始位置以及数据类型的位数来确定的。比如对于无符号int类型的数据来说,倘若我们知道它的起始位置为0x1,而当前的操作系统采取的是大端法规则,假设0x1-0x4的内存地址中存储的字节依次为0xFF,0xFF,0xFF,0xFF,由此计算机将会帮我们计算出这个无符号int类型的值为232-1,也就是无符号int类型的最大值
这其中计算机是根据0x1-0x4这四个字节上的值,以及无符号int类型的解释方式,最终得到的这个无符号int类型的值。
由此可见,计算机在解释一个数据类型的值时主要有四个因素:
对于特定的系统来说,前两种因素都是特定的,而对于后两种因素的改变,则可以改变一个数据类型的值的最终计算结果,这就是强制类型转换。对于大部分高级程序设计语言来讲,都提供了强制类型转换。
比如C语言,我们可以将一个无符号int类型的值强制转换为其它类型,在转换之后,对于上面四个因素之中,改变的是最后两个。
#include <stdio.h>
int main(){
unsigned int x = 0xFFFFFF61;
int *p = &x;
char *cp = (char *)p;
printf("%c\n",*cp);
}
这是一个简单的强制类型转换示例,可以看到我们将一个无符号int类型的值,先赋给了一个int类型的指针,又强制转换成了char类型的指针,最终我们输出这个char类型指针所代表的字符。
结果的输出是一个a
输出a的原因就是由上面的四个因素决定的,我们看这个具体程序上的四个因素。
可以看出,强制类型转换有时候会让结果变的让人难以预料,因此这种技巧一般不太推荐使用,但是这种手段也确实是程序设计语言所必需的。
2.1.4 表示字符串
这一点其实上面我们已经提到了,我们知道9
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。