CS:APP Web Aside DATA: TMIN :Writing TMin in C
写这篇博客是在看到CSAPP第二章,Tmin写法这一部分。为了给自己加深理解,就将官网上的旁注http://csapp.cs.cmu.edu/public/waside/waside-tmin.pdf翻译了一下。中间有些部分跳过,是因为我觉得于Tmin关系不大,或者是写的太繁复。
谢绝转载
1. 案例
在表2-18和练习题2.21中,我们仔细的将Tmin的值写成-2147483647-1。那么为什么我们不将它写成-2147483648或者0x80000000?当我们查看头文件limits.h,发现它和我们使用类似的定义方法来定义Tmin和TMax:
/* Minimum and maximum values a ‘signed int ‘canhold.*/
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX – 1)
不幸的是,补码表示的不对称性和C语言转换规则之间这种奇怪的交互,迫使我们使用奇怪的方式来写Tmin。虽然理解这个问题需要我们专研C语言标准中一些隐晦的角落,但是它也能帮助我们理解整数数据类型和表示的一些细微之处。
假设我们将Tmin写成-2147483648,在32位机器中实现,使用表2-8的数据大小。当编译器遇到-X形式的数字时,首先决定X的数据类型和数据值,然后取反。2147483648对于int而言,超出了表示范围。编译器会尝试寻找一个可以准确表示该数值的数据类型。
Figure 1 :表示整形常量的数据类型。依赖C语言版本以及进制(十进制或十六进制),按照顺序第一个可以准确表示数值的数据类型就是所求的。
Figure 2:TMIN的不同数据类型。依赖C语言版本和进制(十进制或十六进制),我们为两种表达式找到了三种不同的数据类型,包括值是正数的案例。
依照不同的语言版本,它从表一中按照顺序寻找合适的数据类型。对于ISO C90,它从int到long,再到unsigned,直到找到能表示2147483648的数据类型。正如我们在2.3.3看到的那样,在32位机器中,2147483648和-2147483648有相同的位表示,所以最后的数据类型就是unsigned,值是2147483648。而对于ISO C99,最后找到的数据类型是long long,值是-2147483648。
而对于十六进制常量0x80000000,在32 机器上,最后的数据类型都是unsigned,值是0x80000000(等价于2147483648)。
对于64位及其,无论是ISO C90还是ISO C99,十进制形式的数据类型都是long,值是-2147483648,十六进制都是unsigned,值是0x80000000(等价于2147483648)。
所有的不同都已经在表二中总结,对于结果是long或long long 数据类型的,常量都是负值,并且是64位表示。而对于结果是unsigned的数据类型,常量是正数,并且是32位表示。
这些结果可在下面的代码中明显的看出不同:
Int dcomp = (-2147483648 < 0);
Int hcomp = (0x80000000 < 0);
这两行都尝试去表示Tmin,作为十进制或十六进制,并且测试该值是否小于0.依赖于编译器和字的大小,我们可以发现dcomp的值可以是0,也可以是1,这表明十进制常量可能是正数,也可能是负数。但是hcomp的值始终是0,表示十六进制的常量始终是正数。我们看上去很简单的写出Tmin32的任务,比预想的要复杂的多。
练习题1:
考虑下面的代码:
Int dtmin = -2147483648;
Int dcomp2 = (dtmin < 0);
Int htmin = 0x80000000;
Int hcomp = (htmin < 0);
我们在32位和64位机器上,使用两种不同的补码来表示整形,并且使用ISO-C90和ISO-C99两种方式来编译。在所有的情况中,我们发现dcomp和hcomp2始终都是1,进一步的测试表明,dtmin和htmin事实上等于Tmin(大小)。解释为什么这段代码对不同编译器和语言版本的结果都相同。
因为最后都会被转化成int型,那么数值大小就是-2147483648,而不会有歧义。
2 应用
对许多程序而言,由字大小和语言标准引起的模糊不会影响程序的行为(例如问题1)。然而,现在我们可以理解为什么通常的将Tmin 写成-2147483647-1会有一个更合适的结果。因为2147483647就是Tmax的值,因此不需要考虑使用表一的转化规则。