为什么最小的 int-2147483648的类型是‘ long’?

对于一个学校项目,我必须编写 C 函数 printf。一切都很顺利,但有一个问题我找不到一个好的答案,所以我来了。

printf("PRINTF(d) \t: %d\n", -2147483648);

告诉我(gcc -Werror -Wextra -Wall) :

   error: format specifies type 'int' but the argument has type 'long'
[-Werror,-Wformat]
printf("PRINTF(d) \t: %d\n", -2147483648);
~~     ^~~~~~~~~~~
%ld

但是如果我使用一个 int 变量,一切都会很顺利:

int i;


i = -2147483648;
printf("%d", i);

为什么?

编辑:

我理解了很多观点,它们非常有趣。无论如何,我想 printf正在使用 <stdarg.h>库,因此,va_arg(va_list ap, type)也应该返回正确的类型。对于 %d%i,返回的类型显然是 int。这能改变什么吗?

10823 次浏览

在 C 语言中,-2147483648不是一个整数常量。2147483648是一个整数常量,而 -只是应用于它的一元运算符,产生一个常量表达式。2147483648的值不适合 int(它太大了,2147483647通常是最大的整数) ,因此整数常量的类型是 long,这会导致您观察到的问题。如果您想提到 int的下限,可以使用来自 <limits.h>的宏 INT_MIN(便携式方法) ,或者小心地避免提到 2147483648:

printf("PRINTF(d) \t: %d\n", -1 - 2147483647);

问题是 -2147483648不是一个整数文字。它是一个由一元否定运算符 -和整数 2147483648组成的表达式,如果 int是32位的,那么整数 2147483648就太大了,不可能是 int。由于编译器在应用负运算符之前将选择一个适当大小的有符号整数来表示 2147483648,因此结果的类型将大于 int

如果您知道您的 int是32位的,并且希望在不破坏可读性的情况下避免警告,那么使用显式强制转换:

printf("PRINTF(d) \t: %d\n", (int)(-2147483648));

这是在具有32位 int的2补码机器上定义的行为。

为了增加理论上的便携性,使用 INT_MIN代替数字,并让我们知道您在哪里找到了一个非2的补充机器来测试它。


说明一下,最后一段有一部分是开玩笑的。如果你指的是“最小的 int”,那么 INT_MIN绝对是最好的选择,因为 int的大小各不相同。例如,仍然有许多16位实现。写出 -2 31只有在您确定总是精确地表示该值的情况下才有用,在这种情况下,您可能会使用固定大小的类型,如 int32_t,而不是 int

您可能需要一些替代方法来写出十进制的数字,以便让那些可能没有注意到 21474836482174483648之间差异的人更清楚,但是您需要小心。

如上所述,在32位2的补码机器上,(int)(-2147483648)不会溢出,因此是定义良好的,因为 -2147483648将被视为更广泛的有符号类型。然而,对于 (int)(-0x80000000)来说,情况并非如此。0x80000000将被视为 unsigned int(因为它符合无符号表示) ; -0x80000000定义良好(但是如果 int为32位,则 -没有效果) ,并且将得到的 unsigned int 0x80000000转换为 int涉及溢出。为了避免溢出,需要将十六进制常量强制转换为有符号类型: -21474836481。

类似地,如果要使用 left shift 操作符,则需要小心。在32位(或更小) int的32位机器上,1<<31是未定义的行为; 如果 int至少为33位,那么它只能求值为231,因为 k位的左移只有在 k严格小于左参数整数类型的非符号位的数目时才是定义良好的。

1LL<<31是安全的,因为 long long int需要能够表示263-1,所以它的位大小必须大于32。那表格呢

(int)(-(1LL<<31))

可能是最易读的。 YMMV。


对于任何通过的学究,这个问题被标记为 C,最新的 C 草案(n1570.pdf)说,对于 E1 << E2,其中 E1有一个有符号类型,只有当 E1是非负的,并且 E1 × 2E2“在结果类型中是可表示的”,这个值才被定义。(第6.5.7段)。

这与 C + + 不同,在 C + + 中,如果 E1是非负的且 E1 × 2E2“是可表示的,则定义左移操作符的应用程序 在相应的结果类型的无符号类型 中”(& sect; 5.8第2段,加重号)。

在 C + + 中,根据最新的标准草案,如果一个整数值不能在目标类型中表示(& sect; 4.7第3段) ,则将该值转换为带符号整数类型的转换为 实现定义。C 标准的相应段落—— & sect; 6.3.1.3 para 3——说“要么结果是实现定义的,要么产生一个实现定义的信号”