深入理解计算机系统2
第二章 信息的表示和处理
2.1 信息存储
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位.机器级程序将内存视为一个非常大的字节数组,称为虚拟内存(virtual memory).内存的每个字节都由一个唯一的数字来标识,称为它的地址(ad-adress),所有可能地址的集合就成为虚拟地址空间(virtual address space).顾名思义,这个虚拟地址空间只是一个展现给机器级程序的概念性映像
2.1.1 十六进制表示法
2.1.2 字数据大小
2.1.3 寻址和字节顺序
2.1.4 布尔代数
2.1.5 位运算
这里进行一个知识补充:
对一个w位的数移动K位 下面的demo 假设 w = 32
int 1val = OxFEDCBA98<<32;
int aval = OxFEDCBA98>>36;
unsigned uval = OxFEDCBA98>>40;
// 实际上计算机执行的是
int 1val = OxFEDCBA98<<0;
int aval = OxFEDCBA98>>4;
unsigned uval = OxFEDCBA98>>8;
// 这是因为移位实际上是 num <> (k mod w)
// 做了取模运算
2.2 整数表示
我们描述用位来编码整数的两种不同的方式:一种是只能表示非负数,而另一种能够表示负数、零和正数。
2.2.1 整型数据类型
32位程序上C语言整型数据类型的典型取值范围
64位程序上C语言整型数据类型的典型取值范围
C语言标准要求数据类型的取值范围
2.2.2 无符号数的编码
2.2.3 补码编码
2.2.4 有符号数和无符号数之间的转换
C语言允许在各种不同的数字数据类型之间做强制类型转换.
对于32位字长来说,无符号形式的4294967295(UMax32)和补码形式的-1的位模式是完全一样的.将 unsigned强制类型转换成 int, 底层的位表示保持不变.
对于大多数C语言的实现,处理同样字长的有符号数和无符号数之间相互转换的一般规则是:`数值可能会改变,但是位模式不变`
2.2.5 C语言中的有符号数和无符号数
C语言支持所有整型数据类型的有符号和无符号运算.尽管C语言标准没有指定有符号数要采用某种表示,但是几乎所有的机器都使用补码
C语言允许无符号数和有符号数之间的转换.虽然C标准没有精确规定应如何进行这种转换,但大所述系统遵循的原则是底层的位表示保持不变.
2.3 整数运算
2.3.1 无符号加法
2.3.2 补码加法
2.3.3 补码的非
2.3.4 无符号乘法
2.3.5 补码乘法
2.3.6 乘以常数
在大多数机器上,整数乘法指令相当慢,需要10个或者更多的时钟周期,然而其他整数运算(例如加法、减法、位级运算和移位)只需要1个时钟周期.因此,编译器使用了一项重要的优化,试着用移位和加法运算的组合来替代乘以常数因子的乘法.
2.3.7 除以2的幂
在大多数机器上,整数除法要比整数乘法更慢 —— 需要30个或者更多的时钟周期
除以2的幂也可以用移位运算来实现,只不过我们用的是右移,而不是左移.无符号和补码数分别使用逻辑移位和算数移位来达到目的
2.3.8 关于整数运算的最后思考
正如我们看到的,计算机执行的“整数”运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。我们还看到,补码表示提供了一种既能表示负数也能表示正数的灵活方法,同时使用了与执行无符号算术相同的位级实现,这些运算包括像加法、减法、乘法,甚至除法,无论运算数是以无符号形式还是以补码形式表示的,都有完全一样或者非常类似的位级行为。
我们看到了C语言中的某些规定可能会产生令人意想不到的结果,而这些结果可能是难以察觉或理解的缺陷的源头。我们特别看到了unsigned数据类型,虽然它概念上很简单,但可能导致即使是资深程序员都意想不到的行为。我们还看到这种数据类型会以出乎意料的方式出现,比如,当书写整数常数和当调用库函数时。
2.4 浮点数
2.4.1 二进制小数
2.4.2 IEEE浮点表示
2.4.3 舍入
因为表示方法限制了浮点数的范围和精度,所以浮点运算只能近似地表示实数运算.因此,对于值X,我们一般想用一种系统的方法,能够找到"最接近的"匹配值x1,它可以用期望的浮点形式表示出来.这就是舍入(rounding)运算的任务
2.4.5 C语言中的浮点数转换
当在 int、float、double格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设int是32位的)
2.5 小结
Songzhibin