对于学校项目,我要编写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>
librairy,因此,va_arg(va_list ap, type)
也应该返回正确的类型。对于%d
和%i
,显然返回的类型是int
。它会改变什么吗?
答案 0 :(得分:225)
在C中,-2147483648
不是整数常量。 2147483648
是一个整型常量,-
只是一个应用于它的一元运算符,产生一个常量表达式。 2147483648
的值不适合int
(它太大,2147483647
通常是最大的整数)因此整数常量的类型为long
,这会导致你观察到的问题。如果您想提及int
的下限,请使用INT_MIN
中的宏<limits.h>
(便携式方法)或小心避免提及2147483648
:
printf("PRINTF(d) \t: %d\n", -1 - 2147483647);
答案 1 :(得分:59)
问题是-2147483648
不是整数文字。它是由一元否定运算符-
和整数2147483648
组成的表达式,如果int
为32位,则该int
太大而不能成为2147483648
。由于编译器在应用否定运算符之前将选择适当大小的有符号整数来表示int
,因此结果的类型将大于int
。
如果你知道你的printf("PRINTF(d) \t: %d\n", (int)(-2147483648));
是32位,并且希望在不破坏可读性的情况下避免警告,请使用显式强制转换:
int
这是在具有32位INT_MIN
s的2的补码机器上定义的行为。
为了提高理论可移植性,请使用INT_MIN
而不是数字,并告诉我们您在哪里找到非二进制补码机器进行测试。
要明确的是,最后一段部分是个笑话。如果您的意思是“最小的int
”,那么int
肯定是要走的路,因为int32_t
的大小不一。例如,仍有许多16位实现。写出-2 31 只有在您确实总是精确地表示该值时才有用,在这种情况下,您可能会使用固定大小的类型,如int
而不是2147483648
。
你可能想要一些替代方法来写出十进制数字,以便那些可能没有注意到2174483648
和(int)(-2147483648)
之间差异的人更清楚,但你需要小心。
如上所述,在32位二进制补码机器上,-2147483648
不会溢出,因此定义明确,因为(int)(-0x80000000)
将作为更宽的签名类型进行处理。但是,0x80000000
的情况并非如此。 unsigned int
将被视为-0x80000000
(因为它符合无符号表示); -
定义明确(但如果int
为32位,则unsigned int
无效),并且生成的0x80000000
int
转换为{{ 1}}涉及溢出。为避免溢出,您需要将十六进制常量转换为带符号的类型:(int)(-(long long)(0x80000000))
。
同样,如果要使用左移运算符,则需要注意。 {32}机器上的1<<31
是未定义的行为,具有32位(或更小)int
s;如果int
至少为33位,它将仅评估为2 31 ,因为如果k
严格不足,则左移k
位只能很好地定义比左手参数的整数类型的非符号位数。
1LL<<31
是安全的,因为long long int
需要能够代表2 63 -1,所以它的位大小必须大于32.所以表格< / p>
(int)(-(1LL<<31))
可能是最具可读性的。 YMMV。
对于任何通过的学生,这个问题被标记为C,而最新的C草案(n1570.pdf)表示E1 << E2
,其中E1
具有签名类型,该值为仅当E1
为非负且E1 × 2E2
“在结果类型中可表示”时才定义。 (§6.5.7第4段)。
这与C ++不同,其中如果E1
是非负的并且E1 × 2E2
“可表示,则定义左移运算符的应用程序
在结果类型的相应无符号类型中(§5.8第2段,强调添加)。
在C ++中,根据最新的草案标准,如果值无法在目标类型中表示,则将整数值转换为有符号整数类型实现定义(§4.7para) 3)。 C标准的相应段落 - §6.3.1.3段。 3 - 表示“结果是实现定义的或者实现定义的信号被引发”。)