提问人:Box Box Box Box 提问时间:1/25/2023 最后编辑:Box Box Box Box 更新时间:1/25/2023 访问量:250
为什么 gcc 发出警告说常量是无符号的,因为它太大了,而它的类型是 __int128 并且是有符号的?
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 节说:
整数常量的类型是其值可以是相应列表中的第一个 代表。
如果整数常量不能由其列表中的任何类型表示,则它可能具有扩展整数 type(如果扩展整数类型可以表示其值)。如果常量列表中的所有类型 ,则扩展整数类型应进行有符号。如果常量列表中的所有类型 为无符号,则扩展整数类型应为无符号。如果列表同时包含 signed 和 无符号类型,扩展整数类型可以是有符号或无符号。如果整数常量不能 由其列表中的任何类型表示,并且没有扩展的整数类型,则整数常量具有 无类型。
从上面可以清楚地看出,as cannot fit in , 和 ,编译器将尝试有符号的扩展整数类型;As does fit inside ,然后成为整数常量的类型,从第二条警告消息中可以看出。ULONG_MAX
int
long int
long long int
ULONG_MAX
__int128
这都是预期的行为,但我面临的问题是,这显然是有符号类型,正如 C 标准所期望的那样。但是,为什么第一条警告消息(“整数常量太大,以至于它是无符号的”)说常量被视为无符号?这对我来说没有意义,因为根据 6.4.4.1.6 只检查有符号的扩展整数类型,那么整数常量如何被视为无符号呢?__int128
为了澄清这个问题,我的问题不在于;格式警告是预期的,我只是把它留在那里以表明常量是 类型 .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)给出了一个诊断消息,所以它是符合的,严格来说,它不必支持扩展的整数类型,并且它给出了某种诊断信息(“警告:咩咩”会使它们同样符合)。
然而,这是一个小的编译器错误,因为十进制整数常量使用 6.4.4.1 中的带引号的列表: then then .因此,这适用:“如果常量列表中的所有类型都已签名,则扩展整数类型应进行签名。int
long
long long
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));
}
输出:
(signed) __int128_t
-1
如果整数常数“如此之大,以至于它是无符号的”,那么_Generic就会打印“无符号长长”或“某种扩展类型”。同样,在有符号到无符号的转换过程中会得到一个正值。x
结论:gcc 选择了正确的类型,但警告消息不正确。它应该说“整数常数是如此之大,以至于它被扩展了”。
我猜这条消息是 C90 的一些遗留物,其中不存在扩展整数类型。编译时会添加一个额外的警告:-std=c90
警告:此十进制常量仅在 ISO C90 中无符号
这似乎是应始终显示的正确警告。似乎是从 gnu90 切换到 gnu11 作为 gcc 默认选项期间发生的一个小错误。
评论
%lu
unsigned long int
unsigned long long int
%llu
1ul
unsigned long
printf
long
long long
long
long long
printf