【问题标题】:What is the exact value range for unsigned long?unsigned long 的确切值范围是多少?
【发布时间】:2019-07-15 18:34:00
【问题描述】:

我正在完成“Learn C the hard way”一书中的练习。练习 7 要求读者找出使unsigned long 的范围超出的值。

long 更改为unsigned long 并尝试找出使其过大的数字。

所以我的方法是首先在我的机器上获取unsigned long 的大小:

printf("SIZEOF ULONG: %lu", sizeof(unsigned long));

这会打印出8。因此,假设 unsigned long 在我的机器上占用 64 位,我在 Wikipedia 上查找了最大范围。

64 位(word、doubleword、longword、long long、quad、quadword、qword、int64)

  • 无符号:从 0 到 18,446,744,073,709,551,615

我原以为使用上述值声明 unsigned long 会在没有警告的情况下编译,直到我将该值增加 1。但结果不同。编译以下程序会导致警告。

#include <stdio.h>
int main()
{
    unsigned long value = 18446744073709551615;
    printf("SIZEOF ULONG: %lu", sizeof(unsigned long));
    printf("VALUE: %lu", value);
    return 0;
}

bla.c: In function ‘main’:
bla.c:5:27: warning: integer constant is so large that it is unsigned
     unsigned long value = 18446744073709551615;
                           ^~~~~~~~~~~~~~~~~~~~

那么为什么 gcc 抱怨值太大,我以为我已经将它声明为unsigned

【问题讨论】:

  • 你声明了变量的类型,但没有声明值。
  • #include &lt;limits.h&gt; 并使用ULONG_MAX
  • 旁白:你应该使用 %zu 格式说明符来表示类型 size_t 而不是 %lu

标签: c long-integer unsigned


【解决方案1】:

对于不适合 long int(或 long long int,自 C99 和 C++11 起)的值,您需要一个整数文字的后缀。以下任何一项都将符合 unsigned long int:

unsigned long value = 18446744073709551615u;
unsigned long value = 18446744073709551615lu;
unsigned long value = 18446744073709551615ul;

请看这里的后缀表:

https://en.cppreference.com/w/c/language/integer_constant(用于 C) https://en.cppreference.com/w/cpp/language/integer_literal(用于 C++)

【讨论】:

  • @chqrlie - 谢谢,错过了。更新了 C 的参考资料。
【解决方案2】:

整数常量18446744073709551615 太大,无法在您的系统上表示为intlong intlong long int。编译器会警告您将其设为unsigned long long

让我们尝试编译这个程序:

#include <stdio.h>

int main() {
    if (-1 < 18446744073709551615)
        printf("TRUE\n");
    else
        printf("FALSE\n");

    return 0;
}

gcc 确实发出警告:

#1 with x86-64 gcc 8.2
<source>: In function 'main':
<source>:7:14: warning: integer constant is so large that it is unsigned
     if (-1 < 18446744073709551615)
              ^~~~~~~~~~~~~~~~~~~~

程序输出是这样的:

TRUE

clang 生成更明确的警告:

#1 with x86-64 clang 7.0.0
<source>:7:14: warning: integer literal is too large to be represented in a signed integer type, interpreting as unsigned [-Wimplicitly-unsigned-literal]
    if (-1 < 18446744073709551615)
             ^

但是程序输出是:

FALSE

如果18446744073709551615 确实被解释为无符号,就像写成18446744073709551615u0xffffffffffffffff,则必须使用无符号算术进行比较并且失败,因为两个数字具有相同的值。 clang 按照它说的做,但 gcc 没有。

在您的情况下,将值存储到 unsigned long 变量中应该会产生预期的结果,但是向常量添加 u 后缀将确保编译器使用正确的类型对其进行解析。

但是请注意,您可以通过将-1 转换为该类型来获得任何无符号类型的最大值。你也可以使用&lt;limits.h&gt;中定义的宏:unsigned long类型的最大值是ULONG_MAX

【讨论】:

    【解决方案3】:

    编译器分几个步骤处理unsigned long value = 18446744073709551615;。在它可以用一个值初始化value之前,它必须从源代码中读取18446744073709551615并解释它。

    源代码中的数字18446744073709551615 是独立存在的——它不会立即受到以下事实的影响:稍后它将用于初始化value。按照C标准中的规则进行处理。

    这些规则表明,带有十进制数字且没有后缀的数字是 intlong intlong long int,以第一个可以表示该值的为准。由于 18446744073709551615 这么大,它不适合任何这些类型。

    编译器警告您,由于 18446744073709551615 不适合任何这些类型,因此它使用的是无符号类型。在其他情况下,这可能会改变代码的含义。但是,在这种情况下,由于该值会立即用于初始化 unsigned long,因此会产生预期的效果。

    要解决此问题,您可以添加u 后缀,将其更改为18446744073709551615u。对于后缀为u 的十进制数字,C 标准规定该类型是可以表示该值的unsigned intunsigned long intunsigned long long int 中的第一个。

    (C 标准继续说,如果一个值对于列出的类型来说太大了,C 实现可以用扩展的整数类型来表示它,或者它没有类型。没有类型的后果可能是探索很有趣,但这是语言律师问题的主题。)

    【讨论】:

      【解决方案4】:

      如果十进制整数常量在该范围内,则其类型为int,否则为longlong long。它们没有无符号类型,如果值超出这些有符号范围,您会收到警告。您需要为常量添加ul 后缀才能具有正确的类型。

      还有一种更简单的方法可以在不知道其大小的情况下获取此类型的最大值。只需将 -1 强制转换为这种类型。

      unsigned long value = (unsigned long)-1;
      

      【讨论】:

      • 默认情况下,无后缀整数常量的类型为 int 可以忽略它们所具有的类型的层次结构,这取决于它们的值以及它们是十进制还是八进制/十六进制。
      • @ChristianGibbons 同意。更新以更好地反映常量类型规则。
      • l 后缀似乎没有必要。 uU 应该足以使警告静音。
      【解决方案5】:

      use unsigned long value = 18446744073709551615ul; else 18446744073709551615ul 被读作 int 而不是 long

      如果您想知道 unsigned long 中的位数和最大值:

      #include <stdio.h>
      #include <limits.h>
      
      int main()
      {
          printf("number of bits in ULONG: %d\nULONG_MAX = %lu\n",
             sizeof(unsigned long) * CHAR_BIT,
             ULONG_MAX);
          return 0;
      }
      

      编译和执行:

      pi@raspberrypi:/tmp $ gcc -pedantic -Wextra u.c
      pi@raspberrypi:/tmp $ ./a.out
      number of bits in ULONG: 32
      ULONG_MAX = 4294967295
      

      是的,我的 long 不在 64 位上

      【讨论】:

      • 18446744073709551615ul 不是作为int“管理”的,它的类型也不是int18446744073709551615 在 OP 的实现中也不会有 int 类型,任何超过 INT_MAX 的整数常量也不会有 int 类型。
      • 此答案中的文本显示“18446744073709551615ul 被读取为 int 而不是 long”。那是错误的。根据 C 2018 6.4.4.1 5,它是 unsigned long intunsigned long long int,或者是扩展类型,或者,如果不能在其中任何一个中表示,则没有类型。它不是intlong
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      相关资源
      最近更新 更多