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

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

目录

    • 传统艺能😎
    • 确定大小的整型🤔
    • 原码和反码🤔
    • 符号数和无符号数的转换🤔
    • 隐式类型转换🤔
    • 扩展数的位表示🤔

传统艺能😎

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

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

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


在这里插入图片描述

确定大小的整型🤔

对某些程序来说,用确定大小的表示来编码数据时。比如网上通信,数据类型与协议类型兼容是很重要的,我们知道C类型尤其是 long,会面临机器不同取值范围不同的窘况,而C语言恰恰只给出来数据类型的最小范围并没没有给出确切范围,所以程序本身并没有保证移植性。

你多半见过这么些宏:INTN_MIN,INTN_MAX 和 UINTN_MAX,这是根据 ISO C99(也就是我们说的 C99 标准)在文件 stdint.h 中引入的另一类整型。他定义了一组数据类型,他们声明如 intN_t 和 uintN_t,指定的是N位有符号和无符号数,这里的 N具体取值和实现有关,大多数编译器允许的值为 8,16,32,64。因此我们可以理所当然的认为 uint16_t 声明的是 16 位无符号变量。

其实关于整型取值范围和表示最明确的应该是 java 标准。它采用补码表示,java 里面单字节数据类型不再是 char 而是 byte,而且没有 long long 类型,这就让他在不同机器上表现都一样。

原码和反码🤔

这是有符号数的其他两种表示方法。

原码:最高有效位为符号位,用来确定剩下位取正权还是负权。

反码:即原码基础上,符号位不变其他位按位取反得到。他和补码其他都是一样的除了最高位的权是 -(2^(w-1)-1)而不是 -2^(w-1)

这俩表示方式都有一个奇怪的属性,数字 0 有两种不同的编码方式!!!

其实他俩把 0 看成是 000……0 ,而 -0 对于原码是 100……0 ,对于反码是 111……1,几乎现在所有机器都使用补码编译,但过去也有过基于反码表示的机器,且浮点数中有使用原码编码。

还有一个非常有意思的事情,补码英文术语叫 Two’ s complement 而反码叫 Ones’ complement,注意这里两者的撇号各自在 s 的前后,为什么呢?其实对于非负数 x,补码对于 w 位的计算是 2^(w-1),这里只有一个 2,而反码对于 w 位的计算是 111……1 - x,这里有多个 1,就是这么来的。

我们举一个代码作为实例:
在这里插入图片描述
在大端机器上得到的结果是 30 39 和 cf c7,表明 x 的十六进制为 0x3039,mx 的十六进制为 0xcfc7,将他们展开为二进制,x 的位模式为 0011000000111001,mx 为 1100111111000111,值为 12345 和 -12345。

在这里插入图片描述

有符号数和无符号数的转换🤔

C语言允许不同数据类型之间的强制转换,比如定义另一个 char a,(int)a就把一个 char 类型转换成了 int 类型。从数学角度讲,可以想象到集中不同的规则,很明显对于在两种形式中都能表示的值,我们是想保持不变的;将负数转换成无符号数可能会得到 0,如果转换的无符号数太大以至超出了补码表示范围,可能就是TMax。C语言实现来讲,我们思考这个问题不应该从数的角度,一个从位级出发。
在这里插入图片描述
得到结果:

v = -12345,uv = 53191

很明显强制类型转换并没有实际改变原本数据类型,只是改变了解释这些位的方式。可以看出 -12345 的16位补码和 53191 的16位无符号表示是一样的,但是不改变位的表示。

在这里插入图片描述

得到结果:
u = 4294967295,tu = -1

这里的 u 就是32位系统下的 UMax 值,他和 -1 的补码表示一模一样。

所以我们可以总结出:C语言实现下,强转的规则是数值可能会改变但是位模式是一样的

隐式类型转换🤔

C语言支持所有类型的有符号数和无符号运算。尽管C语言并没有指定有符号数要采用某种表示,但几乎所有的机器都采用补码的表示方法。通常大多数的数字都默认是有符号的,比如声明 12345 或者 0x1a2b 时,这个值就会被认为是有符号的,要创建无符号数时必须加上后缀字符 ‘U’ 或者 ‘u’,比如 12345u 或者 0x1a2bu。C语言运行有符号数和无符号数之间的转换 ,转换的原则是底层的位保持不变。

显式强制类型转换就会导致转换发生,就像下面代码:

在这里插入图片描述
另外,当一种类型的表达式被赋值给另一种变量时,就会发生隐式类型转换,就像下面一样:

在这里插入图片描述
当用 printf 输出时,分别用 %d,%u,%x 以十进制,无符号十进制,十六进制输出,注意 printf 并没有使用任何类型信息,所以他可以用 %u 来输出类型 int 的数值,也可以用 %d 输出任何类型为 unsigned 的数值,比如下面代码:

在这里插入图片描述
结果如下:
x = 4294967295 = -1
u = 2147483648 = -2147483648

这两中情况下,printf 首先将这个字当作一个无符号数输出,然后把他当作一个有符号数输出,由于C语言具备这种同时处理有符号数和无符号数的机制,如果一个运算里面包含一个有符号数和一个无符号数,那他会隐式的将有符号数转换为无符号数,并假设两个数都是非负数,对于标准运算符没多大的差异,但是对于像 这类关系运算符来说就会导致非直观结果,比如下表给出的一些求值来表现C语言升级规则的效果, * 标注的是受隐式类型转换影响的:

在这里插入图片描述
比如 -1< 0U, 因为 0U 是无符号数,所以 -1 也会被隐式类型转换成无符号数,等价于 4294967295U < 0U。

扩展数的位表示🤔

常见的运算是在不同字长的证整数之间转换,同时又保持数值不变,当然,数据类型太小以至于无法表示时,这是根本不可能的。然而从一个较小的数据类型转换到一个较大的类型,这是可能的。

将一个 unsigned 转换为更大的类型,我们只需要在开头无脑的加上 0 即可,这种运算被称为 零扩展。将一个补码数字转换为一个更大的数据类型可以执行 符号扩展规则是在表示中添加最高有效位的值(的副本)

比如这个代码:
在这里插入图片描述
得到结果:
sx = -12345:cf c7
usx = 53191: cf c7
x = -12345: ff ff cf c7
ux = 53191:00 00 cf c7

尽管 -12345 补码表示和 53191 无符号16位表示是一样的,但在32位字长时却是不同的,-12345 的十六进制为 0x0000cfc7。前者使用了符号扩展——最开头加了 16 位来补足,都是最高位的 1,表示为 16 进制就是 0xFFFF。最开头使用 16 个 0 来扩展就是 0x0000。

在这里插入图片描述

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