【问题标题】:Why does GCC give a warning when setting an unsigned long to 2^64-1?为什么将 unsigned long 设置为 2^64-1 时 GCC 会发出警告?
【发布时间】:2017-11-09 16:02:06
【问题描述】:

C 标准规定 long int 至少为 4 个字节 - 在我的系统上为 8 个字节。

这意味着我可以在long 中存储高达 2^63-1 的值,在 unsigned long 中存储高达 264-1 的值。

但是,当使用-Wall 标志编译以下代码时,它会发出警告[Wimplicitly-unsigned-literal]:

int main (int argc, char ** argv) {

  unsigned long a;
  a = 18446744073709551615; // 2^64-1

}

如果我使用 263-1 (9223372036854775807) 代替,它编译时没有警告(如预期 - 263-1 将适合 signed long int) .

对于一个项目,我需要在 unsigned long 中设置最大值,但我发现 (9223372036854775807 << 1) + 1 不会发出此警告。然后我的老师建议我可以使用limits.h中定义的ULONG_MAX,这没有给出任何警告。

为什么我不能在没有警告说它被隐式转换的情况下执行此操作 - 当我显式声明它时?

【问题讨论】:

  • 您需要将文字指定为无符号长整数。 18446744073709551615UL
  • 尝试在你的数字常量后添加ul

标签: c gcc


【解决方案1】:

根据 C 标准,不带后缀的十进制常量的类型是 int、long int 或 long long int,特别是足以表示值的第一个。在您的 C 实现中,这些都不能代表 18446744073709551615,因为它太大了。

为了适应您,编译器将其类型设为unsigned long。从技术上讲,这不符合 C 标准,因此编译器会警告您。

在这种情况下,不会造成任何伤害,因为您将值分配给 unsigned long。但是在某些情况下,使用错误的类型可能会导致问题,因此通常您应该在这些常量上附加一个后缀,以确保它们与预期的使用方式相匹配。在这种情况下,u 就足够了;与无后缀类型一样,编译器将根据数量的大小和类型的功能来决定是使用unsigned int、unsigned long int 还是unsigned long long int。

【讨论】:

    【解决方案2】:

    您明确声明了它,但没有 U,这将使其无符号。由于此值没有带符号的整数常量,因此它隐含地使其无符号,为您提供最好明确表示的信息。

    使用a = 18446744073709551615U; 这样做。

    【讨论】:

    • 我明白了——这是说我输入的数字已被隐式转换——而不是我声明的变量。
    • 不应该是ull吗?或者至少ul?
    • @NikosC.:在这种情况下不需要ul 或ull。 C 标准说,十进制常量的类型会有所调整以适应该值。如果使用u 后缀,则类型将是unsigned int、unsigned long int 或unsigned long long int 中可以表示值的第一个。
    • @NikosC。在我的答案中查看整数文字的提升表。
    【解决方案3】:

    看看这个fancy table from the standard (6.4.4.1p5):

    它解释了整数字面量如何适合整数类型。

    基本上,由于您的文字是十进制且没有后缀,因此它会尝试适合以下类型:

    int
    long int
    long long int
    

    由于它不适合 long long int,因此您会收到警告。

    如果您添加U (u) 后缀(或UL 或UL 或小写变体):

    unsigned long a = 18446744073709551615U;
    

    您不会有这个问题,因为您将进入促销序列:

    unsigned int
    unsigned long int
    unsigned long long int
    

    (或UL 和UL 的适当子集)和文字将适合 在unsigned long int(如果这确实是第一个具有不少于 64 个可用位的无符号类型,通常是这样的话)。

    或者,您可以通过切换到八进制或十六进制文字来关闭警告:

    unsigned long a = 0xFFFFFFFFFFFFFFFF;
    unsigned long octal_a = 01777777777777777777777;
    

    至于这些提升顺序是:

    int
    unsigned int
    long int
    unsigned long int
    long long int
    unsigned long long int
    

    根据链接表,这再次允许它们适合第一个 64 位大型无符号类型(通常是 unsigned long int)。

    你绝对应该不做的是:

    (9223372036854775807 << 1) + 1 //don't do this!
    

    因为无论是否生成警告,这都会通过中断6.5.7p4 来调用未定义的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-25
      • 2022-01-04
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      • 2018-10-26
      相关资源
      最近更新 更多