当常量的类型为 __int128 并且有符号时,为什么 gcc 会发出警告说该常量因太大而无符号?

Ash*_*uja 5 c unsigned gcc constants int128

考虑以下代码:

\n
#include <stdio.h>\n\nint main(void) {\n    printf("%llu\\n", 18446744073709551615);\n    printf("%llu\\n", 18446744073709551615ULL);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译(gcc -std=c18)后,我收到以下警告:

\n
test1.c: In function \xe2\x80\x98main\xe2\x80\x99:\ntest1.c:4:26: warning: integer constant is so large that it is unsigned\n    4 |         printf("%llu\\n", 18446744073709551615);\n      |                          ^~~~~~~~~~~~~~~~~~~~\ntest1.c:4:20: warning: format \xe2\x80\x98%llu\xe2\x80\x99 expects argument of type \xe2\x80\x98long long unsigned int\xe2\x80\x99, but argument 2 has type \xe2\x80\x98__int128\xe2\x80\x99 [-Wformat=]\n    4 |         printf("%llu\\n", 18446744073709551615);\n      |                 ~~~^     ~~~~~~~~~~~~~~~~~~~~\n      |                    |     |\n      |                    |     __int128\n      |                    long long unsigned int\n
Run Code Online (Sandbox Code Playgroud)\n

C标准第 6.4.4.1.5 节和 6.4.4.1.6 节说:

\n
\n

整型常量的类型是可以表示其值的相应列表中的第一个。\n

\n

在此输入图像描述

\n

如果整型常量不能用其列表中的任何类型表示,则它可能具有扩展整型\n类型(如果扩展整型类型可以表示其值)。如果常量列表中的所有类型都有符号,则扩展整数类型也应有符号。如果常量列表中的所有类型均为无符号,则扩展整数类型应为无符号。如果列表同时包含有符号和\n无符号类型,则扩展整数类型可以是有符号或无符号的。如果整型常量不能用其列表中的任何类型表示并且没有扩展整型,则该整型常量没有类型。

\n
\n

从上面可以清楚地看出, asULONG_MAX无法放入int,long intlong long int,编译器将尝试有符号扩展整数类型;asULONG_MAX适合里面__int128,然后成为整数常量的类型,从第二条警告消息可以看出。

\n

这都是预期的行为,但我面临的问题是,这显然__int128是一个有符号类型,正如 C 标准所期望的那样。但是为什么第一条警告消息(“整数常量太大以至于它是无符号的”)说该常量被视为无符号?这对我来说没有意义,因为根据 6.4.4.1.6 仅检查有符号扩展整数类型,那么如何将整数常量视为无符号?

\n
\n

为了澄清一下这个问题,我的问题不在于printf;格式警告是预期的,我只是将其留在那里以表明该常量的类型为__int128

\n

考虑代码:

\n
#include <stdio.h>\n\nint main(void) {\n    __int128 a = 18446744073709551615;\n    unsigned long long b = 18446744073709551615;\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译此命令会发出警告:

\n
test2.c: In function \xe2\x80\x98main\xe2\x80\x99:\ntest2.c:4:22: warning: integer constant is so large that it is unsigned\n    4 |         __int128 a = 18446744073709551615;\n      |                      ^~~~~~~~~~~~~~~~~~~~\ntest2.c:5:32: warning: integer constant is so large that it is unsigned\n    5 |         unsigned long long b = 18446744073709551615;\n      |                                ^~~~~~~~~~~~~~~~~~~~\n
Run Code Online (Sandbox Code Playgroud)\n

我的问题是,既然常量的类型是__int128,为什么编译器说它是无符号的?显然__int128是有符号类型。

\n

Lun*_*din 5

gcc(和 clang)给出了一条诊断消息,因此它是一致的,严格意义上来说,它不必支持扩展整数类型,并且它给出了某种诊断消息(“警告:bleh”将使它们同样一致) )。

然而,这是一个小编译器错误,因为十进制整数常量使用 6.4.4.1 中的引用列表:intthen longthen 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));
}
Run Code Online (Sandbox Code Playgroud)

输出:

(signed) __int128_t
-1
Run Code Online (Sandbox Code Playgroud)

如果整数常量“太大以至于无符号”,那么 _Generic 将打印“unsigned long long”或“某种扩展类型”。同样,x在有符号到无符号的转换期间会得到正值。

结论:gcc 选择了正确的类型,但警告消息不正确。它应该说类似“整数常量太大以至于被扩展”之类的内容。

我猜这个消息是 C90 的一些遗留物,其中扩展整数类型不存在。编译时-std=c90会添加额外的警告:

警告:此十进制常量仅在 ISO C90 中无符号

看来这是应该始终显示的正确警告。似乎是从 gnu90 切换到 gnu11 作为 gcc 默认选项期间发生的一个小错误。