标签: CSAPP
本章研究三种最重要的数字表示:无符号、补码和浮点数。
指针有两个方面:指和类型。指表示某个对象的位置,而类型表示那个位置上所存储对象的类型。
我们将程序称为“32位程序”或“64位程序”时,区别在于该程序是如何编译的,而不是其运行的机器类型。
为避免由于编译器的不同带来的数据类型大小的改变,强烈建议用 int32_t 和 int64_t 代替 int,这样可以准确控制数据表示。
按位进行布尔运算。
| : 或 运算& : 与 运算~ : 非 运算(按位取反)^ : 异或 运算逻辑运算从左向右求值,如果已经能确定表达式的值了,就不会继续往下求值。
C 语言没有明确规定有符号数应该使用哪种类型的右移。所有会遇到可移植性的问题。
对于一个 w 位组成的数据类型,移位 k 位,当 k>w 时,在很多机器上,最终的位移量是 k mod w 位,Java 中按照这个标准进行位移,但是,C 语言对此不做任何保证,所以应该保持位移量小于待位移值的位数。
整数分为 无符号数 和 有符号数。无符号数一般采用原码表示,有符号数一般采用补码表示。
采用补码表示导致了取值范围的 不对称————负数的范围比正数的范围大 1. C 语言并没有要求用补码形式表示有符号整数,但是几乎所有的机器都是这么做的。强制类型转换: 结果保持位值不变,只是改变了这些位的解释方式。
当执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么 C 语言会隐式地将有符号参数强制类型转换为无符号数,并假设这两个数都是非负的,来执行这个运算。
数字的位扩展:整数从较小的类型转换为较大的类型,同时又保持数值大小不变。
扩展 无符号数 零扩展:简单的在表示的开头补零 扩展 补码数 符号扩展:在表示中添加最高有效位的值。截断数字:与位扩展相反,截断是将大类型的数转换为小类型的,但是不能保证数的大小不变。 将一个 w 位的数字 x 截断为 k 位的 y 时:
x 为 无符号数 y=x mod 2k x 为 补码数 将 x 看做无符号数截断以后,将截断的数转化为补码(即将最高位转换为符号位)。由于有符号数到无符号数的隐式转换会导致一些难以察觉的错误,所以新手程序员尽量少用无符号数。
IEEE 浮点标准用 V=(−1)s∗M∗2E 的形式来表示一个数:
符号 s:决定这个数是正数(s = 0)还是负数(s = 1),对于数值 0 的符号位作为特殊情况处理。用 1 位二进制数表示。尾数 M:M是一个二进制小数。用 n 位二进制数表示。位模式是 f=fn−1...f1f0。阶码 E:E 的作用是对浮点数加权这个权重是 2 的 E 次幂(可能是负数)。用 k 位二进制数表示。位模式是 e=ek−1...e1e0。在单精度浮点数(32位表示)中, k=8,n=23 。在双精度浮点数(64位表示)中, k=11,n=52 。IEEE 表示的三种情况:
规格化的值 当 e 的位模式不全为 0,也不全为 1 时,就属于规格化表示。此时, E=e−Bias。其中, Bias=2k−1−1 M=1+f 非规格化的值 当阶码域全为 0 时,就属于这种情况。此时, E=1−Bias M=f 特殊值 当阶码域全为 1 时,属于这种情况。此时: 当小数域全为 0 时,表示无穷, s=0 表示正无穷, s=1 时表示负无穷。当小数域非零时,表示 NaN,即“Not a Number”。计算机将信息编码为 位(比特),通常组织成 字节 序列。有不同的编码方式来表示整数、实数和字符串。不同的计算机模型在编码数字和多字节数据中的字节顺序时使用不同的约定。
C 语言的设计可以包容多种不同字长和数字编码的实现。64 位字长的机器逐渐普及,并正在取代统治市场长达 30 多年的 32 位机器。由于 64 位机器也可以运行 32 位机器编译的程序,我们的重点就放在区分 32 位和 64 位程序,而不是机器本身。64 位程序的优势是可以突破 32 位程序具有的 4GB 地址限制。
大多数机器对整数使用补码编码,而对浮点数使用 IEEE 标准 754 编码。在位级上理解这些编码,并且理解算术运算的数学特性,对于想使编写的程序能在全部数值范围上正确运行的程序员来说,是很重要的。
在相同长度的无符号和有符号整数之间进行强制类型转换时,大多数 C 语言实现遵循的原则是底层的位模式不变。C 语言隐式的强制类型转换会出现许多程序员无法预计的结果,常常导致程序错误。
由于编码的长度有限 与传统整数和实数运算相比,计算机运算具有非常不同的属性。当超出表示范围时,有限长度能够引起数值溢出。当浮点数非常接近于 0.0,从而转换成 0 时,也会下溢。
和大多数其他程序语言一样,C语言实现的有限整数运算和真实的整数运算相比,有一些特殊的属性。例如,由于溢出,表达式 x∗x 能够得出负数。但是,无符号数和补码运算都满足整数运算的许多其他属性,包括结合律、交换律和分配律。这就允许编译器做很多优化。
浮点表示通过将数字编码为 x∗2y 的形式来近似地表示实数。最常见的浮点表示方法是由 IEEE 标准 754 定义的。它提供了几种不同的精度,最常见的是单精度(32位)和双精度(64 位)。 IEEE 浮点数也能表示特殊值。
必须非常小心的使用浮点运算,因为浮点运算只有有限的范围和精度,而且并不遵守普遍的算术属性,比如结合性。