探究 C 语言标准库 limits.h 关于INT_MIN 宏的奇怪定义
最近在读《深入理解计算机系统》(顺便打个广告,卡内基梅隆出品的这本书绝对精品),在书的48页提到了在C语言标准库limits.h中将int类型的最小值INT_MIN定义为-INT_MAX-1。书中提到了为何不写做-2147483648或者0x80000000,但是并没有给出解释,只是说这需要我们钻研C语言标准的一些比较隐晦的角落。
我们先看看MSVC的相关头文件内容:
GCC也给出了类似的定义方式:
为何是这样呢?这个头文件对我来说并不陌生。这几个宏也时常在用着,但是从没有注意过这个细节问题。百度一番后没找到满意的答案,最后勉强找到了http://www.hardtoc.com/archives/119这个比较靠谱的答案。因为英文看起来比较麻烦,再者作者描述的也不是很清楚,我写篇文章 权当翻译 一下,再加上一点点自己查到的内容。
我们先来查看C语言标准文档是怎么解释常量的,ISO/ANSI C99 和C11对于这里的描述是相同的。我们摘录下来:
“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.”
大概意思是“一个常量起始于一个数字,但是不包含小数点或指数的部分。并且可能有一个用于指定数字基底的前缀和一个指定类型的后缀。”
这里的前缀比如16进制的0x,后缀比如指明是长整型的L或者无符号的U。
简而言之,c99对整数的定义是这样的:
1 | D [0-9] |
不知道大家有没有注意到,这里没有提到正负号,或而言之,常量不包含符号。
下一段引用自 幻の上帝:
C语言中的常量(constant)和常量表达式(constant expression)是两个概念。前者是语法规定的一些记号(token);后者语法本质是条件表达式(conditional-expression),只是加了能在翻译而不是运行时求值的语义限制。 C语言中的常量(constant)和常量表达式(constant expression)是两个概念。前者是语法规定的一些记号(token);后者语法本质是条件表达式(conditional-expression),只是加了能在翻译而不是运行时求值的语义限制。
那么,我们假设INT_MIN定义为-2147483648,这里的-2147483648其实是由一个一元运算符-和一个常量2147483648组成,编译器怎么解释2147483648呢?编译器对于这么一个数字,按照定义好的变量顺序一一匹配其类型,这个定义好的顺序在文档里可以查到,C99和C11是一样的,另外C++11也是相同的。截图如下:
首先是由int开始,因为2147483648超过了int的最大取值范围2147483647,所以会被认为是long int类型。按照C语言的表达式求值规则,原本可能使用的x < INT_MIN这样的表达式,x被转换为long int类型再进行表达式求值了,便有可能会出现一些预料之外的结果,而按照-2147483647-1这样的定义,不会被认为是long int类型的,这个小技巧使得我们获得了正确的类型和正确的值。
这样解释不知道大家明白了没有?
这篇文章基本上是查证和翻译了,如果说有版权的话,原始版权归原作者所有。