C语言中TMin的写法

  在看《深入理解计算机系统》第二版中文版时(Computer Systems A Programmer's Perspective Second Edititon),看到48页第二章网络旁注中提到:
C语言中,将TMin32(32位有符号整数的最小值)写成 -2147483647-1。为什么不简单地写成 -2147483648 或者 0x80000000 ?
  书中提到是由于补码表示的不对称性和C语言转换规则之间奇怪的交互。补码表示不对称性CSAPP讲解的通俗易懂,但这里面涉及到什么样的C语言转换规则,书中却没有说明。

  在这篇博文的写作过程中,搜到了一些很有用的资料,请见本文末尾的参考资料一节。本文中很大部分内容都是参照 CS:APP Web Aside DATA:TMIN:
Writing TMin in C 一文来写的,然后根据自己的理解添加了一些细节上的东西,力求能够更加通俗易懂一些,这就是本文存在的意义。

  C语言中整型常量的实际类型

  首先来看一下C语言中整型常量的定义。

  C99标准中 6.4.4.1 Integer constants 中提到:
    An integer constant begins with a digit, but has no period or exponent part. It may have a
prefix that specifies its base and a suffix that specifies its type.

  可见如果不发生溢出,整型常量的值总是非负数。如果前面出现符号,则是对整型常量使用的一元运算符,而不是整型常量的一部分。

  整型常量的实际类型取决于长度、基数、后缀字母和C语言实现确定的类型表示精度。确定整数常量类型的规则比较复杂,并且在非标准C、C89和C99中是不相同的。具体规则可见<<C语言参考手册》第五版 第二章 2.7.1 整型常量一节。对于本文中提到的场景,下面的这个整数常量的类型表就够用了。

        表一:整数常量的类型

 ISO C90 ISO C99
十进制(Decimal) 十六进制(Hexadecimal) 十进制(Decimal) 十六进制(Hexadecimal)
int
long
unsigned
unsigned long
int
unsigned
long
unsigned long
int
long
long long
 
int
unsigned
long
unsigned long
long long
unsigned long long

  根据C语言版本和常量的格式(十进制和十六进制),常量的数据类型是从上面表格里选择第一个最合适(能表示常量而不溢出的)的类型。

  对于ISO C90,编译器依次尝试int 、long、 unsigned(32位机器上long跟int一样,是32位), 最终选择unsigned来表示。对于 2147483648 和 -2147483648,如果表示为32位的二进制数字,它们的位表示是一样的,都是0x80000000。所以这个常量表达式(-2147483648)的数据类型为unsigned且值为 2147483648。

  对于ISO C99,编译器依次选择 int、long、long long,最终选择long long类型才能容纳 2147483648 。用64位,可以唯一表示 2147483648 和 -2147483648,所以这个常量表达式的数据类型为long long,值为 -2147483648。

  对于16进制常数 0x80000000(注意,按照C语言中整型常量的定义,这个整数常量是正数,值为2417483648),在32位机器上,编译器也是利用同样的规则,依照表一中的16进制的列表来处理。两个语言标准中,都是首先跟TMax32(0x7FFFFFFF)比较,由于0x80000000更大,所以这个值不能用int来表示。接下来和UMax32(0xFFFFFFFF)比较,由于比它小一些,所以选择unsigned来表示。所以这个常量表达式的数据类型是unsigned,值为0x80000000(或者说,是等于2147483648)。

  在64位的机器上,事情稍微有些不同。两个语言标准中,十进制的格式 -2417483648 都是long(64位)类型,值为 -2417483648,然而十六进制格式 0x80000000 都是unsigned类型,值为0x80000000(或者说,是 2147483648)。

  用一句话来解释C语言中TMin32的古怪写法的原因:虽然-2147483648 这个数值能够用int类型来表示,但在C语言中却没法写出对应这个数值的int类型常量。

  C语言中如何正确表示TMin32呢?  

  C语言中limits.h中定义了如下两个宏: 

#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)

    ISO C99在stdint.h文件中定义了一些宏 INTN_C、UINTN_C、INTMAX_C与UINTMAX_C,提供对整型常量的长度与类型的可移植性控制。

  知道这个有什么用?:

  1. 考虑如下代码:

int dcomp = (-2147483648 < 0);
int hcomp = (0x80000000 < 0);

  请大家思考一下dcomp和hcomp的值是0还是1?需要考虑编译器指定的C语言版本和机器位数(字的大小)。

  gcc 中,C模式默认的C语言标准是gnu89(ISO C90, 包括一些C99特性)。可以通过 -std 选项来指定C语言的版本。

  MSVC支持C90,只支持部分的C99特性。

  2. 考虑如下代码:

int dtmin = -2147483648;
int dcomp2 = (dtmin < 0);
int htmin = 0x80000000;
int hcomp2 = (htmin < 0);

  如果你亲自在32位和64位机器上用ISO-C90和ISO-C99版本来编译运行一下,会发现,所有情况下,dcomp2和hcomp2的值都是1。你能解释一下为什么吗?

 

  广告一下吧,在搜 TMin 资料的时候,发现了一本书《一站式学习C编程》,网上有第一版的html的版本,我只看了其中介绍整型  的一节,发现作者写的通俗易懂,而且是国人写的,必须得支持啊。

  参考资料:      

    C语言参考手册第五版 2.7.1 整型常量
       深入理解计算机系统 2.2.3 补码编码

    在c语言中写TMin

  补充阅读:    

  由于补码的不对称性,导致在用补码来表示负数的编译器中使用abs()函数会有问题
     如果TMin32/-1,也会导致同样的问题

  整数常量的类型造成的一个隐晦的问题

 


如果您看了本篇博客,觉得对您有所收获,请点击右下角的“推荐”,让更多人看到!

资助Jack47写作,打赏一个鸡蛋灌饼钱吧
pay_weixin
微信打赏
pay_alipay
支付宝打赏

 

posted on 2013-01-06 09:12  生栋  阅读(7687)  评论(6编辑  收藏  举报

导航