探究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对整数的定义是这样的:

D                        [0-9]
IS                       (u|U|l|L)*
{D}+{IS}?
 

不知道大家有没有注意到,这里没有提到正负号,或而言之,常量不包含符号。

下一段引用自 幻の上帝:

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类型的,这个小技巧使得我们获得了正确的类型和正确的值。

这样解释不知道大家明白了没有?

这篇文章基本上是查证和翻译了,如果说有版权的话,原始版权归原作者所有。

时间: 2025-01-30 12:10:16

探究C语言标准库limits.h关于INT_MIN宏的奇怪定义的相关文章

C标准库参考指南(5)limits.h

5. limits.h 该头文件定义了变量类型的特点. 5.1. Defined Values 下面这些值由#define指令定义.这些值由实现所确定,但一般不会比下面给出的值小. 英文原文:http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.5.html 原文作者:Eric Huss 中文译者:柳惊鸿 Poechant 版权声明:本文的原文版权归Eric Huss所有,中文译文版权归Poechant所有.转载请注明来自"柳大的CSDN博客&quo

c头文件limits.h各种常量的范围问题

问题描述 c头文件limits.h各种常量的范围问题 软件:VS2013 Os:win8.1 代码 写的有点乱.. #include<stdio.h> #include<stdlib.h> #include<limits.h> int main() { printf("CHAR_BIT =%d CHAR_MAX = %d CHAR_MIN = %d SCHAR_MAX = %d SCHAR_MIN = %d UCHAR_MIN = %d UCHAR_MAX =

c语言的头文件#include &amp;lt;limits.h&amp;gt;

c语言的头文件#include <limits.h> CHAR_BIT Number of bits in the smallest variable that is not a bit field. 8 SCHAR_MIN Minimum value for a variable of type signed char. –128 SCHAR_MAX Maximum value for a variable of type signed char. 127 UCHAR_MAX Maximum

C标准库&lt;ctype.h&gt;实现

1.背景知识 ctype.h是C标准函数库中的头文件,定义了一批C语言字符分类函数(C character classification functions),用于测试字符是否属于特定的字符类别,如字母字符.控制字符等 我们经常将字符排序并分成不同的类别,为了识别一个字母,可以编写: if('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z') ...... 当执行字符集是ASCII码的时候,可以得到正确的结果,但

c语言-C语言标准库fwrite失败,代码已给出,求帮助。

问题描述 C语言标准库fwrite失败,代码已给出,求帮助. 代码: #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> void main() { FILE* fp = fopen("aaa.txt", "rb+"); if (fp == NULL) { return; } if (_fseeki64(fp, 0, SEEK_END) != 0) { return; } long long size =

随机io-Go语言标准库文件随机IO带缓存么?

问题描述 Go语言标准库文件随机IO带缓存么? 就是说,func (f *File) ReadAt(b []byte off int64) (n int err error)func (f *File) WriteAt(b []byte off int64) (n int err error)这两个函数在标准库中,有没有带缓冲区?底层是用的C的标准IO(带缓冲区)么? 解决方案 用bufiohttp://golang.org/pkg/bufio/ 解决方案二: 谢谢,bufio顾名思意是带buf

请用C++或C#的类(class)重新实现在list.h和和list.c中定义的AList类型及其操作函数??

问题描述 请从以下三道试题中选择两题完成,并在收到试题的一周内发回结果.其中第二题为必选,并且答题中至少有一道需要用C#完成.我们将以两题的得分之合作为你的测试结果.有完成三题者,则以其中两道得分最高的分数之合作为你的测试结果.你的测试的结果将对我们在是否聘用你以及聘用你的待遇上起非常重要的参考作用.谢谢你的合作和支持.1.请用C++或C#的类(class)重新实现在list.h和和list.c中定义的AList类型及其操作函数.2.CHANGES.txt包含了我公司开发人员对一个代码库的修改历

C标准库&lt;signal.h&gt;实现

背景知识 signal.h是C标准函数库中的信号处理部分, 定义了程序执行时如何处理不同的信号.信号用作进程间通信, 报告异常行为(如除零).用户的一些按键组合(如同时按下Ctrl与C键,产生信号SIGINT).信号是程序执行过程中发生的异常事件,同步信号的产生 是因为程序自身的某些动作,例如除零或不正当的访问存储器,异步信号是由程序外部的行为引起的,比如有人敲击了提示键或者另外一个程序(异步地执行)给你 的程序发信号,都会引发一个异步信号.程序不能屏蔽的信号要求立即得到处理.如果不对发生的信号

C标准库&lt;assert.h&gt;实现

1.背景知识 头文件<assert.h>唯一的目的就是提供assert宏定义,可以在程序中关键的地方使用这个宏来进行断言.如果一处断言被证明非真,希望程序在标准错误流输出一条适当的提示信息,并使执行异常终止. 可以这样写代码: #include<assert.h> ... assert(0 <= i && i < sizeof(a) / sizeof(a[0])); 当然上面的代码不是实战中的最好的形式,程序异常终止应该改为某种错误的恢复. 宏NDEBU