信息的存储与值的计算
前言
我们非常难想象出来,计算机仅仅能识别0和1这样再简单只是的数字,却给人们带来了巨大的变化.对于无法与人脑相比的计算机来所,简单的1和0就是适合他们的数字.只是呢,一个1或者1个0往往代表不了什么意义,,他们必须被赋予上下文,才干有详细的含义.比方,假设我们知道1和0代表的布尔类型的值,那么我们就知道1是true,0是false.
对于二进制所表示的数字来说,主要有三种,即无符号,补码以及浮点数.
因为计算机对于固定类型的二进制数字往往都是有位数限制的,比方int类型使用四个字节(32位二进制)来表示,因此在计算的时候,会发生溢出的情况,最简单的是我们使用无符号整数0xFFFFFFFF与无符号的0xFFFFFFFF相乘会溢出.
在产生溢出的时候,得出的结果往往令人大跌眼镜.比方两个正数相乘可能得到负值,两个正数相加也可能得到负值.而对于不同的计算机来所,因为数值范围可能有所不同,因此掌握信息相关的内容对于写出扩平台的程序来所也是非常有帮助的.
信息的存储
大多是计算机使用8位的块,或者说字节,来作为最小的可寻址的存储器单位,问不是在存储器中訪问单独的位.换句话说,我们在訪问存储器的内容时,最小的訪问单位通常是字节.
在我们变成的时候,往往会把虚拟存储器抽象为一个字节数组,而每个数组内的元素或者说字节都有唯一的地址,这些地址的集合就被称为虚拟地址空间.虚拟地址空间是为了给机器级的程序一个概念上的映像,实际上为了提供这个映像,内部的实现是很复杂的.
十六进制表示法
对于机器来说,可能比較喜欢0和1,可是对于人类来说,1和0就不够看了.由于通产情况下,为了便于阅读,我们会使用十六进制去表示二进制.1位十六进制的数字能够表示4位二进制数字,因此一个字节就能够表示为0x00--0xFF.
有关十六进制,二进制,十进制之间的转换你得弄明确,要想明确加减乘除一样的记住.
每台计算机都有一个字长,啥是字长?你须要先明确字的概念,因为虚拟地址空间的地址就是使用一个字来表示的,因此OS中的字长就决定了虚拟地址空间的大小.字长就是用于指明整数和指针数据的标准大小.
比方在32位的OS下,最大的内存就是232 = 4 * 210 * 210 * 210 B = 4GB,
在64位的计算机上,最大的内存是234GB.
这就代表了假设你的电脑是32位的OS,则表示最大的可用内存就是4G,你就算有个1000G的内存条也是白搭,仅仅能使用4G.相对而言呢,说是64位的OS能支持到2的32次方G,可是依据实际来说,你电脑的各个硬件能支持到16G就不错了,我的电脑支持8G就到头了...
数据大小
因为计算机位数的不同,会造成在数据类型的存储上,採用的位数略有不同,下图表示了在32位和64位的机器下,C语言其中的数字数据类型须要的位数.
我相信你仅仅要不瞎,应该能看出在32位和64位的OS下所需的位数不同吧,这是C语言的数据类型.对于长整型以及字符指针来说,在32位和64位系统下的字节数时不同.特别的,对于指针类型来说,全部指针类型在32位下都是4位,而在64位下都是8位,这是由虚拟地址空间的地址位数或者说字长所决定的.
寻址和字节顺序
对于跨越多个字节的程序对象(程序对象指令,数据或者控制信息等,是程序其中对象的通常)来说,我们须要指定两个规则,才干唯一确定一个程序对象的值.
比方对于int类型的值0xFF来说,假设我们要依据虚拟内存地址去获取这个整数值,那么首先我们须要知道他的气势虚拟内存地址是多少.另外,我们还须要知道,对于表示int类型的四个字节来说,这四个字节的排列顺序.
对于第一个问题,因为大部分计算机都採用连续的内存地址去存储一个程序对象,因此我们称内存地址中最小的那个就是该程序对象的起始地址,也是该程序对象的地址.
对于第二个对象,一把有两种方式,即大端法和小端法.对于一个整数0x00000FF,我们如果他的起始地址为0x1,那么对于使用大端法规则的系统来说,0x1-0x4的虚拟内存所存储的值依次为0x00,0x00,0x00,0xFF,相反对于採用小端发规则的系统来说,0x1-0x4的虚拟内存所存储的值依次为0
xFF,0x00,0x00,0x00.
强制类型转换
对于一个特定的数据类型来说,计算机再解释这类数据的值的时候,是依据起始位置以及数据类型的位数来确定的.比方对于无符号的int类型的数据来说,倘若我们知道他的起始位置为0x1,而当前的OS採取的是大端法规则,如果0x1-0x4的内存地址中存储的字节依次为0xFF,0xFF,0xFF,0xFF由此计算机将会帮助我们计算出这个无符号int类型的值为65535,也就是无符号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的原因就是由上面那四个因素造成的(位排列顺序,起始位置,数据类型的字节数,数据类型的解释方式.)
1.cp指针的值与x变量的事实上内存地址相等.(起始位置)
2.我的OS是小端表示法,也就是说如果x变量的事实上内存地址为0x1,那么0x1-0x4的值分别为0x61,0xFF,0xFF,0xFF.(位排列顺序)
3.char仅仅占一个字节,因此会仅仅读取0x61这个值.(数据类型的字节数,或者说大小)
4.0x61为十进制的97,相应ASCII表的话,代表的字符就是a,因此终于输出了a.(数据类型的解释方式)
能够看出,强制类型转换有的时候会让结果变得难以预料,因此这样的技巧一般不太推荐使用,可是这样的手段也确实是程序设计语言所必须的.
字符串的表示
这一点事实上我们已经知道了,我们刚才知道了97事实上代表的是字符’a’,而这个的由来就是依据ASCII表来表示的,我们在Linux系统上能够输入man ASCII命令来查看.
代码的表示
二进制怎样的表示代码?
事实上这些都是编译器的责任了,我们仅仅须要写出像上面那个小程序一样的人们能够看懂的代码,编译器便会帮我们将其翻译成相应的机器所认识的二进制序列.从这个角度上,程序语言事实上就是一个二进制序列的简单描写叙述,它提供我们更简单的编写计算机能够运行的二进制序列的方法.
补充:
1.怎样查看自己的OS是大端法还是小端法规则?
什么是大端小端?
比方0x11223344在大端机上是11223344,在小端机上是44332211,而一个机器是大端还是小端要看cpu类型以及执行在上面的操作系统。同一款cpu在不同的操作系统使用的大小端情况是不同的。当然我们通常使用的x86+windows是小端。
那怎样測试大小端呢?
通常的技巧是使用一个指针:如:
int x = 1;
if(*(char *)&x == 1)
printf("little-endian\n");
else printf("big-endian\n");
2.字长
字长是的主要技术指标之中的一个。指的是一次能并行处理的二进制位数,字长总是8的整数倍。通常PC机的字长为16位(早期),32位,64位。
PC机能够通过编程的方法来处理随意大小的数字,但数字越大,PC机就要花越长的时间来计算。PC机在一次操作中能处理的最大数字是由PC机的字长确定的。
3.虚拟空间是什么意思?
虚拟内存(Virtual Memory)是指计算机呈现出要比实际拥有的内存大得多的内存量。因此他同意程式员编制并执行比实际系统拥有的内存大得多的程式。这使得很多大型项目也可以在具有有限内存资源的系统上实现。一个很恰当的比喻是:你不必很长的轨道就能让一列火车从上海开到北京。你仅仅须要足够长的铁轨(比方说3公里)就能完毕这个任务。
採取的方法是把后面的铁轨即时铺到火车的前面,仅仅要你的操作足够快并能满足需求。列车就能象在一条完整的轨道上执行。这也就是虚拟内存管理须要完毕的任务。
在 Linux0.11 内核中,给每一个程式(进程)都划分了总容量为 64MB 的虚拟内存空间。
因此程式的逻辑地址范围是 0x0000000 到 0x4000000。有时我们也把逻辑地址称为 虚拟地址。由于和虚拟内存空间的概念类似,逻辑地址也是和实际物理内存容量无关的。逻辑地址和物理地址的“差距”是 0xC0000000。是由于虚拟地址->线性地址->物理地址映射正好差这个值。这个值是由操作系统指定的。
机理逻辑地址(或称为虚拟地址)到线性地址是由CPU的段机制自己主动转换的。
假设没有开启分页管理,则线性地址就是物理地址。假设开启了分页管理,那么系统程式须要參和线性地址到物理地址的转换过程。
详细是通过设置页文件夹表和页表项进行的。