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 C90ISO 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
支付宝打赏

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值