> 文档中心 > 读书笔记Pt.6——《深入理解计算机系统》

读书笔记Pt.6——《深入理解计算机系统》

目录

    • 传统艺能😎
    • 截断数字🤔
    • 关于有符号数和无符号数的建议🤔
    • 无符号加法🤔
    • 补码加法🤔
    • 无符号乘法🤔

传统艺能😎

小编是双非本科大一菜鸟不赘述,欢迎大佬指点江山(QQ:1319365055)
此前博客点我!点我!请搜索博主 【知晓天空之蓝】

🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,打码路上一路向北,背后烟火,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
这是我和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我

🎉🎉🎉倾力打造转码社区微信公众号🎉🎉🎉
在这里插入图片描述


在这里插入图片描述

截断数字🤔

书接上文,当我们扩展一个数值时不采用扩展额外的位来扩展他,就会去减少一个数的位数,比如这样:
在这里插入图片描述
以一台 32 位机器为例,x 强转为 short 类型时,我们就会将 32 位的 int 截断为 16 位的 short,这样表示出来 53191 在 16 位位模式下的补码 sx 就是 -12345 ,然鹅再强转回 int 时,符号扩展把 16 位设置为 1,从而生成 -12345 的 32 位补码 y 表示。

也就是说一个 w 位的数,截断为一个 k 位的数时,理所当然丢到了 w-k 位,这个过程中,截断可能会改变他的值,这也是溢出的一种形式。我们对于一个无符号数 x,截断到 k 位本质上是对其进行取模运算,相当于 x mod 2^k (求 x 除 2^k 的余数)
在这里插入图片描述

关于有符号数和无符号数的建议🤔

就像我们看到的,有符号数到无符号数的隐式类型转换导致了一些非直观结果,而这些非直观结果却导致了直观的错误,其实在日常中这些隐式类型转换的细微错误很难被察觉,因为这是在代码没有明确指示下发生的,我们也非常容易忽略他的影响

很直观的例子就是我们使用的关键词 sizeof ,他的默认返回类型是无符号整型,正因为这个尴尬的属性,所以很多场景里面就会衍生出隐晦的错误。再比如下面这一串代码:
在这里插入图片描述
当这里的 length = 0 时 ,原本 0-1 = -1,这里不符合条件最后会返回一个 0.0 ,但实际上在运行后会发生一个存储器错误,这里返回类型定义为 float 类型,就默认为是一个无符号类型辣! ,辣么表示不了负数的他就会溢出到一个非常大的数,和 32 位的无符号整型溢出是一个道理,导致整个程序整段垮掉。

接下来书中谈及到了函数 getpeername 的安全漏洞,属于 unix 网络编程,我们现在暂且不谈(属于是没能力谈了)。

我们已经看到了芥末多无符号运算的细微特征,尤其是隐式类型转换,会导致错误和漏洞的方式。啊,要想避免他最直接的方法就是不用无符号数,别以为咱搁这儿搞笑,其实除了C语言很少有语言支持无符号整型。很明显这些语言设计者都认为他的弊大于利,因此 java 只支持有符号整数并利用补码运算实现;>> 右移运算符被定义为算术右移(符号位扩充),>>> 被定义为逻辑右移。

上帝创造的每一个东西都绝非绝对,无符号数也不是一无是处,其实我们想把字仅仅看成是位的集合,没有任何数字意义时,那么无符号数就是非常有用的,比如往字里面加入描述各种布尔条件的标记时(flag = 1为真这种类似的),地址自然就变成了无符号的;当实现模运算和多精度运算的数学包时,数字由字的数组来表示,无符号数也会非常有用。

在这里插入图片描述

无符号加法🤔

非负整数 x,y,并且 x >= 0 , y <= (2^w)-1,计算两个 w 位数字相加结果可能产生 w+1 位的结果,计算他们的和我们得到可能的范围在 【0,(2^w+1)-2】,如果保持和为一个 w+1 位的数字,再用他加上另一个数,我们可能需要 w+2 个位来表示,下图是书中对 4 位表示的 x+y 的坐标图,这种持续的== “字长膨胀” ==意味着需要完整的表示算术运算的结果,我们不能对字长做任何限制;有些编程语言支持无限精度的运算,允许在存储器限制之内的任意整数运算,然而更常见的是支持固定精度的运算,因此像加法,乘法这种就不同于他们在整数上的相应运算。

在这里插入图片描述

无符号运算被视为一种模运算形式,加法等同于计算和模上 2^w,通过丢弃 x+y 的 w+1 位来表示最高位,来给你康个好康的,我们就以 9+12 为例,考虑一个四位数字表示 ,9 表示为【1001】,12 表示为【1100】,和为 21 表示为五位的【10101】,丢掉最高位得到 【0101】为十进制的 5,和值 21 mod 16 = 5 是一致的!

一般而言,如果 x+y < 2^w ,和为 w+1 位的表示中最高位会等于 0,因此丢弃他不会改变这个数值,而和为 【2w,2(w+1)】,那么和为 w+1 的位表示的最高位会等于1,因此丢弃他就相当于从中减去一个 2^w,我们把 w 位的 x,y 的整数加法表示为:
在这里插入图片描述

下图就表示了整数加法和无符号加法之间的关系,当 x+y > 2^(w-1) 时和会溢出。
在这里插入图片描述

一个算术运算溢出,就代表着完整的整数结果不能放到数据类型的字长限制中,比如字长 w = 4 的无符号数加法,之前说过和是按模 2^4 = 16 计算的,当 x+y >= 16 时,加法就会溢出,结果相当于从中减去了 16,对应下图中标记为溢出的斜面:

在这里插入图片描述
执行 C 程序时不会将溢出作为错误发出信号,不过有时候我们可以自己判定是否发生了溢出

补码加法🤔

对于补码加法,我们必须确定结果太大或太小时,应该做什么。在一定范围内的 x,y ,他们和整数加法一样,想准确表示出来,可能需要 w+1 位表示,我们通过将表示截断到 w 位来避免数据大小的不断膨胀,然而结果却不像模数加法一样有数学贴切感。

两个数的 w 位补码之和和无符号数之和有完全相同的位级表示。实际上大多数计算机使用同样的机器指令来执行无符号和有符号的加法。因此我们定义字长为 w 的 x,y 在补码上的加法表示为:
在这里插入图片描述

注意 w = 4 时, 2^4 = 16, 负溢出结果会大 16 而正溢出结果会小 16,这里引入一个表大家可以更直观的看到补码加法对比算术加法的不同,我们结果都会截断到 4 位:
在这里插入图片描述
阐明了字长为 4 时的补码加法,运算数范围为 -8~7,x+y < -8 时,加法发生负溢出,导致和增加了 16;x+y 为【-8,8)时,加法会发生正溢出,导致结果小了 16,三种情况又对应发了三个不同的斜面:

在这里插入图片描述

无符号乘法🤔

范围在 x = 2^(w-1) 内的整数 x,y 可以表示为 w 位的无符号数,但他们的乘积 x*y 取值范围从 0~(2(w-1))2 之间,这可能需要 2w 位来表示,不过 C 语言中的无符号乘法被定义为了产生 w 位的值,就是 2w 位的整数乘积的低 w 位所表示的值。

这也看作等价于计算乘积模 2^w,因此,w 位无符号乘法运算表示为:
在这里插入图片描述

今天就到这里吧,润了家人们。