简体   繁体   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?

Consider the following code:考虑以下代码:

#include <stdio.h>

int main(void) {
    printf("%llu\n", 18446744073709551615);
    printf("%llu\n", 18446744073709551615ULL);
    return 0;
}

Upon compilation ( gcc -std=c18 ), I get the following warnings:编译后( 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

The C standard section 6.4.4.1.5 and 6.4.4.1.6 say: C 标准部分 6.4.4.1.5 和 6.4.4.1.6 说:

The type of an integer constant is the first of the corresponding list in which its value can be represented. integer 常量的类型是相应列表中可以表示其值的第一个。

在此处输入图像描述

If an integer constant cannot be represented by any type in its list, it may have an extended integer type, if the extended integer type can represent its value.如果一个 integer 常量不能用其列表中的任何类型表示,它可能有一个扩展的 integer 类型,如果扩展的 integer 类型可以表示它的值。 If all of the types in the list for the constant are signed, the extended integer type shall be signed.如果常量列表中的所有类型都是有符号的,则扩展的 integer 类型应该是有符号的。 If all of the types in the list for the constant are unsigned, the extended integer type shall be unsigned.如果常量列表中的所有类型都是无符号的,则扩展的 integer 类型应该是无符号的。 If the list contains both signed and unsigned types, the extended integer type may be signed or unsigned.如果列表同时包含有符号和无符号类型,则扩展的 integer 类型可能是有符号或无符号的。 If an integer constant cannot be represented by any type in its list and has no extended integer type, then the integer constant has no type.如果 integer 常量不能由其列表中的任何类型表示并且没有扩展 integer 类型,则 integer 常量没有类型。

From the above it is clear that as ULONG_MAX cannot fit in int , long int and long long int , the compiler will try the signed extended integer types;从上面可以清楚地看出,由于ULONG_MAX不能放入intlong intlong long int ,编译器将尝试签名扩展 integer 类型; as ULONG_MAX does fit inside __int128 , that then becomes the type of the integer constant, as can be seen from the second warning message.由于ULONG_MAX确实适合__int128 ,因此它成为 integer 常量的类型,从第二条警告消息中可以看出。

This is all expected behavior, but the issue I am facing is that clearly __int128 is a signed type, as is expected from the C standard.这都是预期的行为,但我面临的问题是,显然__int128是有符号类型,正如 C 标准所预期的那样。 But then why does the first warning message ("integer constant is so large that it is unsigned") say that the constant is treated as unsigned?但是为什么第一条警告消息(“整数常量太大以至于它是无符号的”)说常量被视为无符号? That makes no sense to me, as according to 6.4.4.1.6 only the signed extended integer types are checked, so how is the integer constant being treated as unsigned?这对我来说毫无意义,因为根据 6.4.4.1.6 仅检查带符号的扩展 integer 类型,那么 integer 常量如何被视为无符号?


To clarify the question a bit, my issue is not with printf ;为了稍微澄清一下这个问题,我的问题不printf the format warning is expected, I just left it there to show that the constant is of type __int128 .格式警告是预期的,我只是把它留在那里以表明常量是__int128类型。

Consider the code:考虑代码:

#include <stdio.h>

int main(void) {
    __int128 a = 18446744073709551615;
    unsigned long long b = 18446744073709551615;
    return 0;
}

Compiling this gives the warning:编译它会给出警告:

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;
      |                                ^~~~~~~~~~~~~~~~~~~~

My issue is that since the constant is of type __int128 , why does the compiler say it is unsigned?我的问题是,既然常量是__int128类型,为什么编译器说它是无符号的? Obviously __int128 is a signed type.显然__int128是有符号类型。

gcc (and clang) gives a diagnostic message so it is conforming, in the strict sense that it doesn't have to support extended integer types and that it gave some sort of diagnostic message ("warning: bleh" would have made them equally conforming). gcc(和 clang)给出了一条诊断消息,因此它符合要求,严格来说,它不必支持扩展的 integer 类型,并且它给出了某种诊断消息(“warning: bleh”会让它们同样符合要求).

This is however a minor compiler bug, since decimal integer constants use the the quoted list in 6.4.4.1: int then long then long long .然而,这是一个小的编译器错误,因为十进制 integer 常量使用 6.4.4.1 中的引用列表: int然后long然后long long Therefore this applies: "If all of the types in the list for the constant are signed, the extended integer type shall be signed."因此这适用:“如果常量列表中的所有类型都已签名,则扩展的 integer 类型将被签名。”

gcc 12.2 does behave like that too, as we can see from this demo: 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: Output:

(signed) __int128_t
-1

Had the integer constant been "so large that it is unsigned", then _Generic would have printed "unsigned long long" or alternatively "some extended type".如果 integer 常量“太大以至于它是无符号的”,那么 _Generic 将打印“unsigned long long”或“某些扩展类型”。 Similarly, x would have gotten a positive value during signed to unsigned conversion.类似地, x在有符号到无符号的转换过程中会得到一个正值。

Conclusion: gcc picks the correct type but the warning message is incorrect.结论:gcc 选择了正确的类型,但警告消息不正确。 It should say something like "integer constant is so large that it is extended".它应该说“整数常量太大以至于它被扩展”之类的话。

I would guess this message is some remain from C90 where extended integer types didn't exist.我猜这条消息是一些来自 C90 的遗留信息,其中扩展的 integer 类型不存在。 Compiling with -std=c90 adds an additional warning:使用-std=c90编译会添加一个额外的警告:

warning: this decimal constant is unsigned only in ISO C90警告:此十进制常量仅在 ISO C90 中是无符号的

It would appear that this is the correct warning that should always be displayed.看起来这是应该始终显示的正确警告。 Seems to be a minor bug that occurred during the switch from gnu90 to gnu11 as default option for gcc.似乎是在从 gnu90 切换到 gnu11 作为 gcc 的默认选项期间发生的一个小错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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