为什么gcc会给予一个警告,说常量是无符号的,因为它太大了,而它的类型是__int128,并且是有符号的?

sgtfey8w  于 2023-01-26  发布在  其他
关注(0)|答案(1)|浏览(282)

请看下面的代码:

  1. #include <stdio.h>
  2. int main(void) {
  3. printf("%llu\n", 18446744073709551615);
  4. printf("%llu\n", 18446744073709551615ULL);
  5. return 0;
  6. }

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

  1. test1.c: In function main’:
  2. test1.c:4:26: warning: integer constant is so large that it is unsigned
  3. 4 | printf("%llu\n", 18446744073709551615);
  4. | ^~~~~~~~~~~~~~~~~~~~
  5. test1.c:4:20: warning: format ‘%llu expects argument of type long long unsigned int’, but argument 2 has type __int128 [-Wformat=]
  6. 4 | printf("%llu\n", 18446744073709551615);
  7. | ~~~^ ~~~~~~~~~~~~~~~~~~~~
  8. | | |
  9. | | __int128
  10. | long long unsigned int

第6.4.4.1.5节和第6.4.4.1.6节规定:
整型常量的类型是可以表示其值的相应列表中的第一个类型。

如果整型常量不能用其列表中的任何类型表示,则它可以具有扩展整型,前提是扩展整型可以表示其值。如果常量列表中的所有类型都有符号,则扩展整型也应该有符号。如果常量列表中的所有类型都无符号,扩展的整数类型应该是无符号的。如果列表包含有符号和无符号类型,则扩展的整数类型可以是有符号的或无符号的。如果整数常量不能由其列表中的任何类型表示并且没有扩展的整数类型,则整数常量没有类型。
从上面可以清楚地看出,由于ULONG_MAX不适合intlong intlong long int,编译器将尝试signed扩展整数类型;由于ULONG_MAX不适合__int128,因此它变成整数常量的类型,如从第二条警告消息中可以看到的。
这都是预期的行为,但我面临的问题是__int128显然是一个带符号类型,正如C标准所预期的那样。("integer constant is so large that it is unsigned")表示该常量被视为无符号常量?这对我来说毫无意义,因为根据6.4.4.1.6,只检查有符号扩展整数类型,那么整型常量是如何被当作无符号的呢
为了稍微澄清一下这个问题,我的问题不在于printf;格式警告是预期的,我只是把它留在那里,以显示常量的类型是__int128
考虑代码:

  1. #include <stdio.h>
  2. int main(void) {
  3. __int128 a = 18446744073709551615;
  4. unsigned long long b = 18446744073709551615;
  5. return 0;
  6. }

编译此代码会给出警告:

  1. test2.c: In function main’:
  2. test2.c:4:22: warning: integer constant is so large that it is unsigned
  3. 4 | __int128 a = 18446744073709551615;
  4. | ^~~~~~~~~~~~~~~~~~~~
  5. test2.c:5:32: warning: integer constant is so large that it is unsigned
  6. 5 | unsigned long long b = 18446744073709551615;
  7. | ^~~~~~~~~~~~~~~~~~~~

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

zf2sa74q

zf2sa74q1#

gcc(和clang)给出了诊断消息,因此它是符合的,严格意义上说,它不 * 必须 * 支持扩展整数类型,并且它给出了某种诊断消息("警告:bleh "本来会让它们同样符合)。
这是一个编译器的小错误,因为十进制整数常量使用www.example.com中的带引号列表:int then long then long long。6.4.4.1: int then long then 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."
gcc 12.2的行为也是如此,正如我们从这个演示中看到的:

  1. #include <stdio.h>
  2. int main (void)
  3. {
  4. _Generic(18446744073709551615,
  5. long long: puts("long long"),
  6. unsigned long long: puts("unsigned long long"),
  7. __int128_t: puts("(signed) __int128_t"),
  8. default: puts("some extended type") );
  9. typeof(18446744073709551615) x = -1;
  10. printf("Value: %d Size: %zu\n", (int)x, sizeof(x));
  11. }

输出:

  1. (signed) __int128_t
  2. -1

如果整型常量"太大以至于没有符号",那么_Generic将打印"unsigned long long"或"某个扩展类型"。类似地,x将在有符号到无符号转换期间获得正值。
结论:GCC选择了正确的类型,但警告消息不正确,它应该说类似"整型常量太大,以至于被扩展了"的内容。
我猜这条消息是C90中没有扩展整数类型时留下的,用-std=c90编译会增加一个额外的警告:
警告:此十进制常量仅在ISO C90中无符号
看起来这是应该一直显示的正确警告。看起来是一个小错误,发生在从gnu90切换到gnu11作为gcc的默认选项期间。

展开查看全部

相关问题