[英]Why does gcc give a warning saying that the constant is unsigned because it is too large, when it is of the type __int128 and is signed?
考虑以下代码:
#include <stdio.h>
int main(void) {
printf("%llu\n", 18446744073709551615);
printf("%llu\n", 18446744073709551615ULL);
return 0;
}
编译后( gcc -std=c18
),我收到以下警告:
test1.c: In function ‘main’:
test1.c:4:26: warning: integer constant is so large that it is unsigned
4 | printf("%llu\n", 18446744073709551615);
| ^~~~~~~~~~~~~~~~~~~~
test1.c:4:20: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘__int128’ [-Wformat=]
4 | printf("%llu\n", 18446744073709551615);
| ~~~^ ~~~~~~~~~~~~~~~~~~~~
| | |
| | __int128
| long long unsigned int
C 标准部分 6.4.4.1.5 和 6.4.4.1.6 说:
integer 常量的类型是相应列表中可以表示其值的第一个。
如果一个 integer 常量不能用其列表中的任何类型表示,它可能有一个扩展的 integer 类型,如果扩展的 integer 类型可以表示它的值。 如果常量列表中的所有类型都是有符号的,则扩展的 integer 类型应该是有符号的。 如果常量列表中的所有类型都是无符号的,则扩展的 integer 类型应该是无符号的。 如果列表同时包含有符号和无符号类型,则扩展的 integer 类型可能是有符号或无符号的。 如果 integer 常量不能由其列表中的任何类型表示并且没有扩展 integer 类型,则 integer 常量没有类型。
从上面可以清楚地看出,由于ULONG_MAX
不能放入int
、 long int
和long long int
,编译器将尝试签名扩展 integer 类型; 由于ULONG_MAX
确实适合__int128
,因此它成为 integer 常量的类型,从第二条警告消息中可以看出。
这都是预期的行为,但我面临的问题是,显然__int128
是有符号类型,正如 C 标准所预期的那样。 但是为什么第一条警告消息(“整数常量太大以至于它是无符号的”)说常量被视为无符号? 这对我来说毫无意义,因为根据 6.4.4.1.6 仅检查带符号的扩展 integer 类型,那么 integer 常量如何被视为无符号?
为了稍微澄清一下这个问题,我的问题不printf
; 格式警告是预期的,我只是把它留在那里以表明常量是__int128
类型。
考虑代码:
#include <stdio.h>
int main(void) {
__int128 a = 18446744073709551615;
unsigned long long b = 18446744073709551615;
return 0;
}
编译它会给出警告:
test2.c: In function ‘main’:
test2.c:4:22: warning: integer constant is so large that it is unsigned
4 | __int128 a = 18446744073709551615;
| ^~~~~~~~~~~~~~~~~~~~
test2.c:5:32: warning: integer constant is so large that it is unsigned
5 | unsigned long long b = 18446744073709551615;
| ^~~~~~~~~~~~~~~~~~~~
我的问题是,既然常量是__int128
类型,为什么编译器说它是无符号的? 显然__int128
是有符号类型。
gcc(和 clang)给出了一条诊断消息,因此它符合要求,严格来说,它不必支持扩展的 integer 类型,并且它给出了某种诊断消息(“warning: bleh”会让它们同样符合要求).
然而,这是一个小的编译器错误,因为十进制 integer 常量使用 6.4.4.1 中的引用列表: int
然后long
然后long long
。 因此这适用:“如果常量列表中的所有类型都已签名,则扩展的 integer 类型将被签名。”
gcc 12.2 的行为也是如此,我们可以从这个演示中看到:
#include <stdio.h>
int main (void)
{
_Generic(18446744073709551615,
long long: puts("long long"),
unsigned long long: puts("unsigned long long"),
__int128_t: puts("(signed) __int128_t"),
default: puts("some extended type") );
typeof(18446744073709551615) x = -1;
printf("Value: %d Size: %zu\n", (int)x, sizeof(x));
}
Output:
(signed) __int128_t
-1
如果 integer 常量“太大以至于它是无符号的”,那么 _Generic 将打印“unsigned long long”或“某些扩展类型”。 类似地, x
在有符号到无符号的转换过程中会得到一个正值。
结论:gcc 选择了正确的类型,但警告消息不正确。 它应该说“整数常量太大以至于它被扩展”之类的话。
我猜这条消息是一些来自 C90 的遗留信息,其中扩展的 integer 类型不存在。 使用-std=c90
编译会添加一个额外的警告:
警告:此十进制常量仅在 ISO C90 中是无符号的
看起来这是应该始终显示的正确警告。 似乎是在从 gnu90 切换到 gnu11 作为 gcc 的默认选项期间发生的一个小错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.