前面我们介绍了《深入理解计算机系统》第一章的内容----计算机系统漫游。包括简单介绍了 Hello World 程序在计算机中是如何运行的,存储设备的层次结构以及操作系统的抽象概念。第一章的内容只是对很多概念有个简单了解,所以还是不懂的话也不要紧,后面都会对这些概念进行深入的探究。而这一章我们将介绍《深入理解计算机系统》第二章----信息的表示和处理。
程序=数据结构+算法,所以了解数据结构对我们写程序是非常有帮助的,这一章会详细探讨计算机数据的存储和表示,其中也会有大量的公式推导。不要害怕看不懂,LZ 有很多也是第一遍看不懂,但是后面慢慢推导,多看几遍就看懂了,相信只要具备了高中代数知识的都能看懂。而且了解这些知识之后,你会发现你以前的程序代码很多都不严谨,仿佛在你程序生涯中重新打开了一扇窗。
那么多的不说了,这篇博客我们就来看看计算机中信息的存储和表示。
1、信息的存储
什么是信息?信息是客观事物属性的反映,是经过加工处理并对人类客观行为产生影响的数据表现形式。
那么我们这里也要提一下什么是数据,数据是反应客观事物属性的记录,是信息的具体表现形式。任何事物的属性都是通过数据来表示的,数据经过加工处理后成为信息,而信息必须通过数据才能传播,才能对人类产生影响。
例如,数据2、4、6、8、10、12是一组数据,其本身是没有意义的,但对它进行分析后,就可得到一组等差数列,从而很清晰的得到后面的数字。这便对这组数据赋予了意义,称为信息,是有用的数据。
计算机内所有的信息均以二进制的形式表示,也就是由值0和值1组成的序列。大多数计算机使用8位的块,或者说字节("位(bit)"是电子计算机中最小的数据单位,每一位的状态只能是0或1。8个二进制位构成1个"字节(Byte)"),来作为最小的可寻址的存储器单位,而不是在存储器中访问单独的位。
也就是说我们访问计算机最小的单位是八个位构成的字节,而不是值0或值1的单个位。
程序会将存储器视为一个非常大的字节数组,称为虚拟存储器(virtual memory)。存储器的每一个字节都由唯一的数字来标识,也就是我们说的地址(address),所有可能地址的集合称为虚拟地址空间(virtual address space)
比如 C 语言中的一个指针的值,无论它是指向一个整数、一个结构或是某个其他程序的对象,都是某个存储块的第一个字节的虚拟地址。
编译器和系统运行时是如何将存储器空间划分为更可管理的单元,用来存放不同的程序对象。这个后面会详细介绍。
2、十六进制表示法
一个字节由 8 位组成。在二进制表示法中,它的值域为 00000000——11111111;如果用十进制表示就是0——255。这两种表示法用来描述计算机中的位模式(计算机中所有二进制的0、1代码所组成的数字串)来说都不是很方便。二进制表示法太冗长,而十进制表示法与位模式的互相转化又比较麻烦。这时候 十六进制数产生了,十六进制使用数字‘0’~‘9’,以及字符 ‘A’~'F’来表示16个可能的值。一般是 0x 或者 0X 开头。规则是:借一当十六,逢十六进一。
比如十进制数 175,我们用十六进制表示为 0xAF。
十六进制与十进制、二进制之间的互相转换这里就不详细讲解了,相信了解编程的对这个应该是很熟悉了。
3、字
计算机进行数据处理时,一次存取、加工和传送的数据长度称为字(word),一个字通常由一个或多个(一般是字节的整数位)字节构成,字的位数叫做字长(word size),每台计算机都有一个字长,用来指明整数和指针数据的标称大小(nominal size)。由于虚拟地址空间中的地址就是使用一个字来编码的,因此字长决定了系统的虚拟地址空间的最大大小。
通俗来讲:字长是CPU的主要技术指标之一,指的是CPU一次能并行处理的二进制位数,字长总是8的整数倍,通常PC机的字长为16位(早期),32位,64位。
比如:LZ 打开命令提示符,然后输入 systeminfo,可以看到处理器的位数是 64 位
也就是说 LZ 的电脑字长为 64 位,不过现在基本上 CPU 都是64 位的,但是为了发挥64位字长的优势,我们必须与64位软件相辅相成,比如你得安装64位操作系统,否则的话,在32位软件系统中64位字长的CPU只能当32位用。
4、数据大小
计算机和编译器支持多种不同方式编码的数字格式,比如整数和浮点数,以及其它长度的数字。而且由于计算机位数的不同,会造成计算机在各种数据类型分配的字节数不一样。下图是 C 语言在 32位机器和64 位机器上各种类型所分配的字节数对比。
我们可以看出,对于长整形( long int)以及字符指针类型(char *)来说,在32位和64位系统下的字节数是不同的。所以在编程时,考虑到系统的移植性,这是我们必须要考虑的。
注意:对于指针类型,由于指针指向的是地址,而虚拟空间的大小是由字长决定的。所以在32位系统下,指针大小为4个字节,在64位系统下,指针大小为8个字节。
5、寻址和字节顺序
对于跨越多个字节的程序对象(程序对象指指令、数据或者控制信息等,是程序当中对象的统称)来说,我们需要制定两个规则:
①、这个对象的地址是什么?
②、在存储器中如何排列这些字节?
在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。
比如:假设一个类型为 int 的变量 x 地址为 0x100,也就是说地址表达式 &x 的值是 0x100,那么,x 的 4 个字节将被存储在存储器的 0x100,0x101,0x102,0x103的位置。
第一个规则解决了,那么第二个规则如何排列呢?采用如下两种方式:
小端法:按照从最低有效字节到最高有效字节的顺序存储对象,也就是最低有效字节在最前面。
大端法:和小端法相反。是按照从最高有效字节到最低有效字节的顺序存储对象,也就是最高有效字节在最前面。
回到上面的变量 x,如果假设 x 的低位字节值到高位字节值分别为 67,45,23,01。那么用大端法和小端法表示分别如下:
由于排列字节的方式有两种,那么这就产生问题了。比如当小端法机器产生的数据被发送到大端法机器或者反方向发送的时候就会发现,接收程序里的字节成了反序。
为了避免这种情况,网络应用程序的代码编写必须遵循已建立的关于字节顺序的规则,以确保发送方机器将它的内部表示转换成网络标准,而接收方机器则将网络标准转换为它的内部表示。
6、数据类型
上面我们在讲解第4小点数据大小时,我们给出了 C 语言中很多的数据类型。那么数据类型到底是什么呢?
程序编码中,计算机是不认识我们所定义的数据类型的。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才定义大数据类型,然后编译器编译标记需要申请大内存,就可以充分利用内存。 例如大胖子必须睡双人床,就给他双人床,瘦的人单人床就够了。
那么我们可以根据上面的寻址和字节顺序得出,计算机在解释一个数据类型的值时主要有四个因素:位排列规则(大端或者小端)、起始位置、数据类型的字节数、数据类型的解释方式。
对于特定的系统来说,前两种因素都是特定的,而对于后两种因素的改变,则可以改变一个数据类型的值的最终计算结果,这就是强制类型转换。对于大部分高级程序设计语言来讲,都提供了强制类型转换。强制类型转换有时候会让结果变的让人难以预料,所以我们需要慎用。
7、字符串的表示
在计算机中,对非数值的文字和其他符号进行处理时,要对文字和符号进行数字化,即用二进制编码来表示文字和符号。其中西文字符最常用到的编码方案有ASCII编码和EBCDIC编码。对于汉字,我国也制定的相应的编码方案,比如 GBK,GB2312等。
比如字符 ‘a’ 的 ASCII 码十进制值为 97,在计算机中用二进制表示就是 01100001
8、代码的表示
程序其实就是一个二进制序列的简单描述,编译器会帮我们将其翻译成对应的机器所认识的二进制序列。
9、总结
本篇博客主要讲解了信息在计算机中是如何存储和表示的,需要注意的是我们访问计算机最小的单位是八个位构成的字节,而不是值0或值1的单个位。下一篇博客我们将介绍布尔代数以及C语言中的位运算。