深入理解计算机系统第四版_深入理解计算机系统导读(二)

6bca57e578551dd6b3efddcb9585d2d2.png

国榷被毁,谈迁重写,冒着情感上的打击,重写《cs:app导读》,他来了!

第二章主要是讲数字的存储和表示,以及在计算机底层他们是如何实现加减乘除各种运算的。


1信息存储

1十六进制表示法

cf2ebae510178db18296cc1d8c0da03c.png

一个十六进制和4位二进制一一对应,相互转换遵循对应关系。

2字数据大小

每台计算机都有一个字长,指明指针数据的标称大小,虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。对于一个字长为w位的机器而言,虚拟地址的范围为0-2w-1,程序最多访问2w个字节。

c567a575d1a06fd395c74f3033d5f3b5.png

为了避免依赖“典型”大小和不同编译器带来的奇怪行为,ISO C99引入了int32_t和int64_t(t代表typedef),程序员应该力图使他们的程序在不同的机器和编译器上可移植。

3寻址和字节顺序

机器如何存储对象,有大端法和小端法两种规则。

大端法:最高有效字节在最前面,基于其存储特点,符号位在所表示的数据的内存的第一个字节中,便于快速判断数据的正负和大小(CPU做数值运算时从内存中依顺序依次从低位地址到高位地址取数据进行运算,大端就会最先拿到数据的(高字节的)符号位)。

小端法:最低有效字节在最前面。基于其存储特点,内存的低地址处存放低字节,所以在强制转换数据时不需要调整字节的内容(比如,把int---4字节强制转换成short---2字节,就可以直接把int数据存储的前两个字节给short就行,因为其前两个字节刚好就是最低的两个字节,符合转换逻辑;另外CPU做数值运算时从内存中依顺序依次从低位地址到高位地址取数据进行运算,开始只管取值,最后刷新最高位地址的符号位就行,这样的运算方式会更高效一些)。

ff39077681bc43946dd505c32c54c77d.png

例如十六进制0x01234567,在大端法中按照地址从低到高依次存储01,23,45,67,在小端法中按照地址从低到高依次存储67,45,23,01

4表示字符串

C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组 ,每个字符都有某个标准编码(比如ASCII码)来表示,最后以null结束。

ASCII码一个字节只能表示127个字符。

为了解决全世界各种字符,Unicode编码通常用两个字节(特别偏僻字符使用4个字节),但是这样对ASCII编码造成了极大的浪费。

UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:

9760e4c5cddbc26a1f2f44d570cec535.png

浏览网页的时候,服务器会把动态生成的Unicode转换为UTF-8再传输给浏览器:

6091d9be50712def8c29cd0c41393fc7.png

5表示代码

c928bb712c09a55ae022d0d2bb3859fd.png

同样的程序,在不同的操作系统上编译生成的二进制代码完全不同,因此二进制代码在不同的机器和系统上不能移植。

从机器的角度来看,程序仅仅只是字节序列,机器没有关于原始源程序的任何信息,除了可能有些用来帮助调试的辅助表。

6布尔代数简介

&:与运算

|:或运算

`:非运算

^:异或运算

位向量一个很有用的应用就是表示有限集合,一个位向量[aw-1,···,a1,a0]来对集合编码,通过指定一个位向量掩码,有选择地使能或是屏蔽一些信号,其中某一位位置上为1时,表明信号i是有效的(使能),而0表明该信号是被屏蔽的,因而这个掩码表示的就是设置为有效信号的集合。

7C语言中的位级运算

与、或、非、异或运算同上

一个不适用第三方变量交换a和b的运算

{a=a^b;b=a^b;a=a^b;}

证明参见异或的性质 交换律,结合律,恒等率(a^0=a,a^1=`a)

位级运算的一个常见用法是实现掩码运算,这里掩码是一个位模式,表示从一个字中选出的位的集合。例如位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他字节就被置0,表达式`0将生成一个全1的掩码。这样的代码具有很好的平台移植性,对于一个32位的机器来说,同样的掩码可以写成x&0xFFFFFFFF,0x11111111但是这样的代码是不可移植的。

8C语言中的逻辑运算

逻辑运算包括&&,||,!注意与位运算区分开来

9C语言中的移位运算

左移:x<<k,将x左移k位,左边的丢弃,右边补k个0

逻辑右移:x>>k,将x右移k位,右边的丢弃,左边补k个0

算术右移:x>>k,将x右移k位,右边的丢弃,左边补k个最高有效位的值。

C语言标准并没有明确定义对于有符号数应该使用哪种类型的右移——算术右移或者逻辑右移都可以,但是大多数编译器/机器都对有符号数使用算术右移,另一方面,对于无符号数,右移必须是逻辑的。不管是对于有符号数还是无符号数,右移一位相当于除以2,左移一位相当于乘以2


2整数表示

1整型数据类型

d3bf7b17401b3eb3f6e6a4af473aad8e.png

472db6291611f941979dc6fab377c9e0.png

为什么取值范围是这样?为什么负数表示的范围比正数大1?在后面的编码章节给出解释。

2无符号数的编码

无符号数编码B2U(binary to unsigned)

bb7dfbfd450f58d9176174d8d8cf9ace.png

无符号数编码具有唯一性

3补码编码

补码编码B2T(binary to two’s -complement):

d4097e94a4ab6168a0376ac49e78cda2.png

补码编码具有唯一性

为了使程序具有更好的移植性,在<limit.h>中定义了一组常量,来限定编译器运行在这台机器上的不同整形数据的取值范围。例如INT_MIN、INT_MAX、INT32_MAX、INT64_MIN、UINT_MAX

4有符号数和无符号数之间的转换

C语言允许强制类型转换,强制类型转换的结果是保持位值不变,只是改变了解释这些位的方式。

b252af2d956f19e664e2c30e760a9959.png

将一个补码转换为无符号数,如果这个补码本身就是表示的正数那么重新解释后仍然是原数,如果这个补码本身表示的是负数,那么重新解释后的值就是原数+2w

18ceafb345a3c81bf878573d3e516ac4.png

5C语言中的有符号数与无符号数

C语言允许强制类型转换,转换之后底层的位不变,改变的是解释这些位的方法。

6扩展一个数字的位表示

要将一个无符号数转换为一个更大的数据类型,我们只需要简单地在表示开头添加0,这种运算称为零扩展。

44484971aead281ae64e39bf4500f9df.png

补码数字转换为一个更大的数据类型,可以执行一个符号扩展,在表示中添加最高有效位的值,表示为如下原理,蓝色表示符号位。

708e13d5407a10d5da9c042e47e93f52.png

7截断数字

对于无符号数,截断就是直接丢弃前面的位。

6b83a378d1638b5d23e2422282483b23.png

对于补码数,截断就是直接丢弃前面的位,然后按照补码数解释。

8关于有符号数与无符号数的建议

有符号数到无符号数的隐式强制类型转换经常导致某些难以发现的错误,这点要特别注意。


3整数运算

1无符号加法

两个无符号数相加,丢弃进位。

721470d6d3e5867d9f02dea975b715b3.png

当执行C程序时,不会将溢出作为错误而发信号,不过有的时候,我们可能判断希望是否发生了溢出。

6563882d7956e14eba6c1b27060e0d1f.png

2补码加法

补码加法,发生正溢出或者负溢出丢弃进位。

f4693dab0c1f8ea90b8332a02299f701.png

检测补码加法中的溢出:

041410118fc9554d688769035b4e45cc.png

3补码的非

补码的非,在每个阿贝尔群中,每个元素都存在一个加法逆元。

3c153a8f98839065ea3f0b6c0ae32bb4.png

4无符号乘法

两个w位的无符号数相乘,就是2w位的整数乘积的低w位来表示。

38522a5026174266bb782f65200b0458.png

5补码乘法

0ce3493624cc1a76397ba4c03a118792.png

C语言中的有符号乘法是通过将2w位的乘积截断为w位来实现的,将一个补码数截断为w位相当于先计算该值模2w,再把无符号数转换为补码。

6乘以常数

乘以一个常数,C语言编译器用移位、加法、和减法的组合来消除很多整数乘以常数的情况,例如一个程序包含表达式x*14,利用14=23+22+21,编译器会将乘法重写为(x<<3)+(x<<2)+(x<<1),将一个乘法替换为三个移位和两个加法。

7除以2的幂

无符号和补码数分别使用逻辑移位和算术移位来达到目的。对于无符号数,最后的结果如果包含小数,正数将会向下取整,负数将会向上取整,也就是说,他们都会在数轴上向零舍入。

对于补码数,如果x>=0,效果与逻辑右移是一样的,如果x<0。当执行算术移位时,需要在移位之前“偏置”这个值,来修正这种不合适的舍入。用表达式表达如下:

(x>0?x:x+(1<<k)-1) >>k

总结:对于无符号数的除法总是舍入到0,直接逻辑右移即可。对于有符号数的除法使用算术右移,根据是正数还是负数,执行(x>0?x:x+(1<<k)-1) >>k

同乘法不同,我们不能用除以2的幂的除法来表示除以任意常数K的除法。

8关于整数运算的最后思考

计算机执行的“整数”运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。我们还可以看到,补码提供了一种既能表示正数又能表示负数的灵活方法,使得它可以像无符号数一样,通过位级运算来实现加法、减法、乘法、甚至除法。


4浮点数

1二进制小数

同十进制编码

2IEEE浮点数表示

IEEE浮点标准用V=(-1)s*M*2E的形式来表示一个数:

  • 符号:s决定这数是负数(s=1)还是正数(s=0)
  • 阶码:E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数),用k位的阶段字段exp=ek-1…e1e0编码阶码E。
  • n位小数字段frac编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0

b802f543c093da58525da96d9eb4c2a9.png

情况1:规格化的值,当exp的位模式不全为0(数值0),也不全为1,都属于这类情况。在这种情况中,阶码字段被解释为以偏置形式表示的有符号整数,E=e-Bias,其中e是无符号数,Bias=2k-1-1的偏置值。

小数字段frac被解释为描述小数值,以二进制表示。尾数定义为M=1+f,也即M隐含以一开头,既然第一位总是等于1,那么我们就不需要显示地表示它。

情况2:非规格化的值,当阶码域为全0时,所表示的是非规格化形式,在这种情况下,E=1-Bias,而尾数的值是M=f,也就是小数字段的值,不包含隐含的开头的1

情况3:特殊值是当阶码全为1的时候,当小数域全为0时,得到的值表示无穷,当s=0时是正无穷大,s=-1时是负无穷大,当小数域为非零时,结果值被称为“NaN”(Not a Number)

3数字示例

参见《深入理解计算机系统》图2-35和课后习题2.47,理解浮点数如何编码。

4舍入

因为表示方法限制了浮点数的范围和精度,所以浮点运算只能近似表示实数运算。

常见的四种舍入方式如下

bcb691a7f37c5d95048ed7c784bb706f.png

IEEE浮点数格式定义了四种不同的舍入方式,默认的方法是找到最接近他们的匹配,而其他三种可用于计算上界和下界。

5浮点运算

IEEE标准制定了一个简单的规则来对浮点数运算,把浮点数x和y看成实数,运算定义成O,计算将产生Round(xOy)

浮点数加法运算、乘法运算不具有结合性,具体参见2.4.5节中的举例及解释,弄懂为什么。

6C语言中的浮点数

C语言标准不要求机器使用IEEE浮点,所以没有标准的方法来改变舍入方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值