Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
whinc opened this issue Nov 1, 2020 · 0 comments
Open

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

whinc opened this issue Nov 1, 2020 · 0 comments
Labels

Comments

@whinc
Copy link
Owner

whinc commented Nov 1, 2020

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 类型的取值范围、常量类型分配、类型提升等知识点。

参考

@whinc whinc added the C/C++ label Nov 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant