【问题标题】:"Integer constant is so large that it is unsigned" compiler warning rationale“整数常量太大以至于它是无符号的”编译器警告理由
【发布时间】:2021-03-08 12:01:16
【问题描述】:

以下 C/C++ 代码:

    long long foo = -9223372036854775808LL; // -2^63

使用警告编译 (g++)

整数常数太大了,它是无符号的。

clang++ 给出了类似的警告。

感谢此错误报告:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661。我现在明白为什么 GCC 会给出这个警告。不幸的是,对错误报告的响应并没有很好地解释这种行为的原因。

问题:

  • 为什么 32/16/8 位有符号整数常量的等效代码没有给出警告?
  • GCC 和 Clang 都给出了这个警告,所以这显然是故意的行为,而不仅仅是在响应错误报告时建议的“使其更易于解析”。为什么?
  • 这种行为是 C/C++ 标准规定的吗?其他标准?

【问题讨论】:

  • @AndrewHenle 对于十进制常量来说,这绝不是一个奇怪的警告,因为0x8000000000000000LL 不会生成警告,但它太大了以至于它没有符号。但在这种情况下,标准将其包含在定义明确的行为中,即使它并不明显。
  • @phuclv 是的,这绝对是重复的,但是让我们冷静一下,看看是否应该关闭这篇文章,或者你链接的那个应该被关闭,因为这个帖子是骗人的。特别是,链接的帖子没有对实际标准提供任何参考,只有 cppreference.com。另外,我认为这里的这个问题比被骗的问题更清楚。
  • @phuclv 谢谢,我看过那篇文章,但显然没有仔细阅读。
  • @AndrewHenle 整数常量的类型是如何定义的并不明显。我认为假设 2,147,483,648 被提升为 unsigned int 然后导致 -2,147,483,648 给出相同的编译器警告是完全合理的(尽管我同意使用 short 是一个坏例子)。我不明白这与解析令牌有什么关系,这就是对错误报告的响应所关注的内容。所以是的,我确实认为它是盲目的。
  • @AndrewHenle 此外,误解某事并急于解释是不会眨眼的。当被问到时拒绝正确解释某事(或承认你不知道答案),是眨眼的。

标签: c++ c gcc integer clang


【解决方案1】:

这与整数常量的类型如何定义有关。

首先,正如 gcc 错误报告中提到的,-9223372036854775808LL 实际上是两个标记:一元运算符- 和整数常量9223372036854775808LL。所以警告只适用于后者。

C standard 的第 6.4.4.1p5 节指出:

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

基于此,无后缀的十进制整数常量将根据值具有intlonglong long 类型。这些都是有符号的类型。因此,任何小到适合 8 位或 16 位类型的值仍然具有 int 类型,而对于 32 位有符号 int 而言太大的值将具有 longlong long 类型,具体取决于类型的大小在那个系统上。带有LL 后缀的常量也是如此,但只尝试了long long 类型。

出现警告是因为您使用的值不适合上述类型列表。任何较小的值都将导致该值具有有符号类型,这意味着不会转换为无符号。

【讨论】:

    【解决方案2】:

    正如错误报告中各种或多或少有些困惑的人所说,整数常量 9223372036854775808LL 太大而无法容纳在 long long 中。

    对于十进制常量,该标准在 6.4.4.1(参见 answer by @dbush)中有一个列表,描述了编译器将尝试赋予整数常量的类型。在这种情况下,类型的唯一有效选项是 (signed) long long,它不适合那里。然后该表下的第 6 节开始执行:

    如果一个整数常量不能用它的列表中的任何类型表示,它可能有一个 扩展整数类型,如果扩展整数类型可以表示它的值。 /--/
    如果列表同时包含有符号和无符号类型,则扩展整数类型 可以签名也可以不签名。

    扩展整数类型在标准中是一个模糊但正式的术语。在这种情况下,编译器显然会尝试将常量压缩到适合的unsigned long long“扩展整数类型”中。这并不是真正保证的行为,而是实现定义的。

    然后将一元 - 运算符应用于产生警告的 unsigned long long

    这就是limits.h等库头喜欢将LLONG_MIN定义为的原因

    #define LLONG_MIN (-9223372036854775807LL - 1)
    

    您可以执行类似的操作来避免此警告。或者更好的是,使用LLONG_MIN

    【讨论】:

    • unsigned long long 不是扩展整数类型。编译器正在实现一个确认扩展,其中代码格式不正确,但它仍然通过使用unsigned long long 接受它。由于代码格式不正确,因此需要发出诊断(警告算作诊断)。
    • @Brian 与将其交叉标记为 C 和 C++ 的人交谈。这个答案是关于 C 的。在 C 中没有什么叫做符合扩展或格式错误。
    【解决方案3】:

    为什么 32/16/8 位有符号整数常量的等效代码没有给出警告?

    常量不限于 8、16 或 32 位。它是第一种适合的类型,十进制 常量可以达到至少 63 位。

    9223372036854775808LL 超出 OP 的 long long 范围,因为 9223372036854775808 占用 64 位。

    -之后应用常量。

    int,long,long long 作为32,32,64 位实现上:-2147483648long long 类型,而不是int


    GCC 和 Clang 都给出了这个警告,所以这显然是故意的行为,而不仅仅是在响应错误报告时所建议的“使其更易于解析”。为什么?

    没有评论。链接没有提供信息。最好在这里数据。


    这种行为是 C/C++ 标准规定的吗?其他标准?

    是的,按照 C 标准。

    【讨论】:

      猜你喜欢
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多