Skip to content

C 语言 <limits.h> 头文件中 INT_MIN 奇怪定义的分析 #21

@whinc

Description

@whinc

C 语言标准库<limit.c>中定义了整型数的最大值和最小值,以 GCC 编译器为例,其定义如下:

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

这里产生一个疑问:为什么INT_MIN不直接定义成 -2147483648,而是写成 -2147483647-1 这种形式呢?

这里涉及到 C 语言如何定义常量,C 语言标准文档对常量的解释如下:
“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.”

大概意思是“一个常量起始于一个数字,但是不包含小数点或指数的部分。并且可能有一个用于指定数字基底的前缀和一个指定类型的后缀。”这里的前缀比如0xFF中的0x,后缀比如100UL中的UL。从整型常量的词法定义也可看出:

C整型常量

如果将INT_MIN定义为 -2147483648,这里的 -2147483648 其实是由一元运算符-和常量 2147483648 组成。编译器遇到常量会按照定义好的变量顺序一一匹配其类型(如果指定了后缀,会从指定类型开始匹配),这个定义好的顺序在 C 语言规范文档里可以查到。

C十进制类型

常量 2147483648 不带后缀,从int类型开始匹配,因为 2147483648 (2^31) 超过了int的最大取值范围 2147483647(2^31-1),为了保证数值正确性,需要表示范围更大的类型,编译器继续向下匹配到long int,其最大取值范围是 2^63-1 足以表示常量 2147483648,所以常量 2147483648 的最终类型被确定为long int。虽然最小值是正确的,但是类型却不正确,这导致的结果是sizeof INT_MIN的大小是 8 字节而非 4 字节,此外在进行 x < INT_MIN这样的比较操作时,x 会被提升到long int类型,这些显然都是不正确的行为。而如果使用常量-2147483647-1定义INT_MIN,其可以被正确的识别为 int 类型。

下面是一组测试代码,演示了两种表示方法虽然最小值一样,但是类型却不同。

#include <stdio.h>

int main() {
    // 2147483647 在 int 的表示区间内,类型为 int,大小为 4 子节
    printf("%d, %d\n",-2147483647-1,  sizeof(-2147483647-1)); 
    // 2147483648 超出 int 的最大值 2147483647,类型提升为 long int,大小为8子节
    printf("%d, %d\n",-2147483648, sizeof(-2147483648)); 
    return 0;
}

运行结果

-2147483648, 4
-2147483648, 8

小结一下,通过分析 C 标准库中INT_MIN的宏的奇怪定义,发现其中牵扯出了 int 类型的取值范围、常量类型分配、类型提升等知识点。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions