深入理解计算机系统
原文地址:http://www.code-farmer.tech/archives/csapp2
GITHUB地址:https://github.com/You-Hmily/note-blog/blob/master/docs/computer-science/CSAPP/2%E3%80%81%E4%BF%A1%E6%81%AF%E7%9A%84%E8%A1%A8%E7%A4%BA%E5%92%8C%E5%A4%84%E7%90%86.md
第二章:信息的表示和处理
现代计算机存储和处理的信息以二进制信号表示。
我们研究三种最重要的数字表示。 1、无符号数(unsigned)编码基于传统的二进制表示法,表示大于或者等于零的数字。补码(two's-complement)编码是表示有符号整数的最常见的方式,有符号整数就是可以为正或者为负的数字。浮点数(floating-point)编码是表示实数的科学记数法的以2为基数的版本。
1、信息存储
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组,称为虚拟内存(virtual memory)。内存的每个字节都由一个唯一的数字来标识,称为它的地址(address),所有可能地址的集合就称为虚拟地址空间(virtual address space)。
1.1、十六进制表示法
一个字节由8位组成。二进制和十进制用来描述位模式都不是非常方便。二进制太冗长,而十进制表示法与位模式的互相转化很麻烦。所以用16为基数的十六进制(hexadecimal)数,来表示位模式。
在C语言中,以Ox或者OX开头的数字常量被认为是十六进制的值。编写机器级程序的一个常见任务就是在位模式的十进制、二进制和十六进制表示之间人工转换。二进制和十六进制之间的转换比较简单直接,因为可以一次执行一个十六进制数字的转换。数字的转换可以参考上图的表。一个简单的窍门是记住十六进制数字A、C和F相应的十进制值。而对于把十六进制值B、D和F转换成十进制值,则可以通过计算它们与前三个值得相对关系来完成。
比如,假设一个数字0X173A4C,将它转换为二进制格式如下所示:
十六进制 1 7 3 A 4 C
二进制 0001 0111 0011 1010 0100 1100
这样就得到了二进制表示:00010111001110101100
反过来,一个二进制数字1111001010110110110011,可以通过首先将它分为每4位一组来转换位十六进制。最左边一组可以少于4位。前面用0补足。如下所示:
二进制 11 1100 1010 1101 1011 0011
十六进制 3 C A D B 3
这样就得到了十六进制表示:0x3CADB3
1.2、字数据大小
每台计算机都有一个字长(word size),指明指针数据的标称大小(normal size)。因为虚拟地址是以这样的一个字长来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。也就是说,对于一个字长w位的机器而言,虚拟地址的范围0~2w-1,程序最多访问2w个字节。
C语言支持整数和浮点数的多种数据格式。如下图展示了为C语言各种数据类型分配的字节数。
CSAPP 2-2.png
1.3、寻址和字节顺序
对于跨域多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在内存中如何排列这些字节的。在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用的字节中最小的地址。例如,假设一个类型为int的变量x的地址为0x100,也就是说,地址表达式&x的值为0x100。那么,(假设数据类型int为32位表示)x的4个字节将被存储在内存的0x100、0x101、0x102和0x103位置。
排列表示一个对象的字节有两个通用的规则。某些机器选择在内存中按照从最低有效字节到最高有效字节的顺序存储对象,而另一些机器则按照从最高的有效字节到最低有效字节的顺序存储。前一种规则--最低有效字节在最前面的方式,称为小端法(little endian)。后一种规则--最高有效字节在最前面的的方式,称为大端法(big endian)
假设变量x的类型为int,位于地址0x100处,它的十六进制值为0x01234567。地址范围0x100~0x103的字节顺序依赖于机器类型:
CSAPP 2-3.png
1.4、表示字符串
C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组。每个字符都由某个标准编码来表示,最常见的是ASCII字符码。因此,如果我们以参数"12345"和6(包括终止符)来运行例程show_bytes,我们得到结果31 32 33 34 35 00。在使用ASCII码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字节大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性。
1.5、表示代码
不同的机器类型使用不同的且不兼容的指令和编码方式。即使是完全一样的进程,运行在不同的操作系统上也会有不同的编码规则,因此二进制代码是不兼容的。二进制代码很少能在不同机器和操作系统组合之间移植。
1.6、布尔代数简介
二进制值是计算机编码存储和操作信息的核心,所以围绕数值0和1的研究已经演化出了丰富的数学知识体系。如布尔代数。
1.7、C语言中的位级运算
C语言的一个很有用的特性就是它支持按位布尔运算。C语言中所使用的:|就是OR(或)、&就是AND(与)、~就是NOT(取反),而^就是EXCLUSIVE-OR(异或)。
位级运算的一个常见用法就是实现掩码运算,这里掩码是一个位模式,表示从一个字中选出的位的集合。让我们来看一个例子,掩码0xFF(最低的8位为1)表示一个字的低位字节。位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他的字节就被置为0。比如,对于x=0x89ABCDEF,其表达式将得到0x000000EF。表达式~0将生成一个圈全1的掩码,不管机器吗的字大小是多少。尽管对于一个32位机器来说,同样的掩码可以写成0xFFFFFFFF,但是这样的代码是不可以移植的。
1.8、C语言中的逻辑运算
C语言还提供了一组逻辑运算符||、&&和!,分别对应命题逻辑中的OR、AND和NOT运算。逻辑运算很容易和位级运算相混淆,但是它们的功能是完全不同的。逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE。它们返回1或者0,分别表示结果为TRUE或者为FALSE。
逻辑运算符&&和||与它们对应的位级运算&和|之间第二个重要的区别是,如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。因此,例如,表达式a&&5/a将不会造成被0除,而表达式p&&*p++也不会导致间接引用空指针。
1.9、C语言中的移位运算
C语言还提供了一组移位运算,向左或者向右移动位模式。对于一个位表示为[Xw-1,....,X0]的操作数x。
2、整数表示
我们描述用位来编码的整数的两种不同的方式:一种智能表示非负数,而另一种能够表示负数、零和正数。
2.1、整形数据类型
C语言支持多种整形数据类型---表示有限范围的整数。
CSAPP 2-5.png
每种类型都能用关键字来指定大小,这些关键字包括char、short、long,同时还可以指示被表示的数字是非负数(声明为unsigned),或者可能是负数。
C语言标准定义了每种数据类型必须能够表示的最小值得取值范围。特别地,除了固定大小的整数类型是例外,我们看到它们只要求正数和负数的取值范围是对称的。此外,数据类型int可以用2个字节的数字来实现,而这几乎回退到了16位机的时代。还可以看到,long的大小可以用4个字节的数字来实现,对32位程序来说这是很典型的
C和C++都支持有符号和无符号数。Java只支持有符号数。
2.2、无符号数的编码
CSAPP 2-6.png
2.3、补码编码
对于许多应用,我们还是希望表示负数值。最常见的有符号数的计算机表示方式就是补码(two's-complement)形式。在这个定义中,将字的最高有效位解释为负权(negative weight)。我们用函数B2Tw(Binary to Two's-complement的缩写,长度为w)来表示:
CSAPP 2-7.png
C语言标准并没有要求要用补码形式表示有符号整数,但是几乎所有的机器都是这么做的。
2.4、有符号数和无符号数之间的转换
C语言允许在各种不同的数字数据类型之间做强制转换。对于大多数C语言实现,处理同样字长的有符号数和无符号数之间相互转换的一半规则是:数值可能会改变,但是位模式不变。
CSAPP 2-8.png
CSAPP 2-9.png
2.5、C语言中的有符号数和无符号数
C语言支持所有整型数据类型的有符号和无符号运算。
2.6、扩展一个数字的位表示
要将一个无符号数转换为一个更大的数据类型,我们只要简单在表示的开头添加0。这种运算被称为零扩展(zero extension),表示原理如下:
CSAPP 2-10.png
2.7、截断数字
CSAPP 2-11.png
3、整数运算
3.1、无符号加法
无符号加法.png
3.2、补码加法
补码加法.png
3.3、补码的非
3.4、无符号乘法
无符号乘法.png
3.5、补码乘法
补码乘法.png
3.6、乘以常数
乘以常数.png
3.7、除以2的幂
乘以2的幂.png
4、浮点数
浮点数表示形如V=x2y的有理数进行编码。它对执行涉及非常大的数字(|V|>>0)、非常接近于0(|V|<<1),以及更普遍的作为实数运算的近似值的计算,是很有用的。
4.1、二进制小数
十进制表示法:
4.2、IEEE浮点表示
IEEE浮点标准用V=(-1)sM*2E 的形式表示一个数:
符号(sign) s决定这数是负数(s=1)还是正数(s=0),而对于数值0的符号位解释作为特殊情况处理。
尾数(significant)M是一个二进制小数,它的范围是12-E或者01-E
阶码(exponent)E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。将浮点数的位表示划分为三个字段,分别对这些值进行编码:
一个单独的符号位s直接编码符号s。
k位的阶码字段exp=e(k-1)...e(1)e(0)编码阶码E。
n位小数字段frac=fn-1....f1f0编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0
4.3、舍入
因为表示方法限制了浮点数的范围和精度,所以浮点数运算只能近似的表示实数运算。因此对于值x,我们一般想用一种系统的方法,能够找到“最接近的”匹配值x',它可以用期望的浮点形式表示出来。这就是舍入运算
5、小结
关于信息的表示先看到这边吧,还有很多不理解。以后还是要多看几遍的!!!未完待学!
网友评论