繁体   English   中英

为什么 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 说:

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

在此处输入图像描述

如果一个 integer 常量不能用其列表中的任何类型表示,它可能有一个扩展的 integer 类型,如果扩展的 integer 类型可以表示它的值。 如果常量列表中的所有类型都是有符号的,则扩展的 integer 类型应该是有符号的。 如果常量列表中的所有类型都是无符号的,则扩展的 integer 类型应该是无符号的。 如果列表同时包含有符号和无符号类型,则扩展的 integer 类型可能是有符号或无符号的。 如果 integer 常量不能由其列表中的任何类型表示并且没有扩展 integer 类型,则 integer 常量没有类型。

从上面可以清楚地看出,由于ULONG_MAX不能放入intlong intlong 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM