为什么 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?

提问人:Box Box Box Box 提问时间:1/25/2023 最后编辑:Box Box Box Box 更新时间:1/25/2023 访问量:250

问:

请考虑以下代码:

#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 节说:

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

enter image description here

如果整数常量不能由其列表中的任何类型表示,则它可能具有扩展整数 type(如果扩展整数类型可以表示其值)。如果常量列表中的所有类型 ,则扩展整数类型应进行有符号。如果常量列表中的所有类型 为无符号,则扩展整数类型应为无符号。如果列表同时包含 signed 和 无符号类型,扩展整数类型可以是有符号或无符号。如果整数常量不能 由其列表中的任何类型表示,并且没有扩展的整数类型,则整数常量具有 无类型。

从上面可以清楚地看出,as cannot fit in , 和 ,编译器将尝试有符号的扩展整数类型;As does fit inside ,然后成为整数常量的类型,从第二条警告消息中可以看出。ULONG_MAXintlong intlong long intULONG_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

c gcc 常量 unsigned int128

评论

2赞 Some programmer dude 1/25/2023
该格式仅适用于一个值,正如编译器告诉您的那样。对于值,您需要 format 说明符 。您在图像中的标准部分与整数后缀有关,例如 创造一个价值,而不是关于格式。%luunsigned long intunsigned long long int%llu1ulunsigned longprintf
0赞 Box Box Box Box 1/25/2023
@Someprogrammerdude这不是问题,但我的问题是第一个警告。此外,在我的系统上,两者都是 8 个字节宽,所以我想我是否使用 或 没有区别。longlong longlonglong long
1赞 VLL 1/25/2023
GCC 不支持 128 位整数文字:stackoverflow.com/questions/60860827/...如何打印 128 位整数:stackoverflow.com/questions/11656241/...
3赞 Lundin 1/25/2023
既然你提到了它,这个编译器警告似乎是一个错误。符合要求的编译器不应为有符号十进制常量选取无符号类型。实际上,gcc 和 clang 似乎都是不合格的。不过对我来说仍然是 BC(咖啡之前),所以我还不敢大喊编译器错误。
0赞 Some programmer dude 1/25/2023
由于使用了错误的格式,您仍然有未定义的行为printf

答:

5赞 Lundin 1/25/2023 #1

gcc(和 clang)给出了一个诊断消息,所以它是符合的,严格来说,它不必支持扩展的整数类型,并且它给出了某种诊断信息(“警告:咩咩”会使它们同样符合)。

然而,这是一个小的编译器错误,因为十进制整数常量使用 6.4.4.1 中的带引号的列表: then then .因此,这适用:“如果常量列表中的所有类型都已签名,则扩展整数类型应进行签名。intlonglong 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 默认选项期间发生的一个小错误。