无符号长整型的确切值范围是什么?

5
我正在完成《学习C语言的困难之路》一书中的练习。第7个练习要求读者找到一个值,使得unsigned long的范围超出限制。

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

因此,我的方法是首先获取我的计算机上unsigned long的大小:

printf("SIZEOF ULONG: %lu", sizeof(unsigned long));
这将打印8作为结果。因此,假设在我的机器上,unsigned long将占用64位,我查找了维基百科上的最大范围。 64位(字、双字、长字、长长、四重、四字、int64)
  • 无符号:从0到18,446,744,073,709,551,615
我原本以为声明一个具有上述值的unsigned long会在未增加值之前编译而不产生警告。然而结果是不同的。编译以下程序会产生警告。
#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了呢?


1
你声明了变量的类型,但没有声明值。 - tadman
4
请使用 #include <limits.h> 并使用 ULONG_MAX - pmg
2
另外:对于类型为size_t的变量,应该使用%zu格式说明符,而不是%lu - Weather Vane
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
8

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

还有一种更简单的方法可以获得此类型的最大值,而无需知道其大小。只需将-1转换为此类型即可。

unsigned long value = (unsigned long)-1;

1
仅仅说未加后缀的整数常量默认具有 int 类型,忽略了它们实际上依据其值以及是否为十进制、八进制或十六进制而拥有的类型层级结构。 - Eric Postpischil
@ChristianGibbons 同意。更新以更好地反映常量类型规则。 - dbush
1
“l”后缀似乎是不必要的。使用“u”或“U”就足以消除警告。 - chqrlie

5

当整数值超出long int(或自C99和C++11以来的long long int)的范围时,需要为整数字面量添加后缀。以下任何一种方式都可以符合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语言的参考资料。 - flu

1
编译器对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实现可以使用扩展整数类型来表示它,或者它没有类型。没有类型的后果可能很有趣,但这是一个语言律师问题的主题。)

0

使用 unsigned long value = 18446744073709551615ul;,否则 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 类型,它的类型也不是 int。在 OP 的实现中,18446744073709551615 也不会有 int 类型,任何超过 INT_MAX 的整数常量都不会有 int 类型。 - Eric Postpischil
1
这个答案中的文本说:“18446744073709551615ul被读作int而不是long”。这是错误的。根据C 2018 6.4.4.1 5,它是一个“unsigned long int”,一个“unsigned long long int”,或者是一个扩展类型,如果在这些类型中都无法表示,则没有类型。它不是一个“int”或“long”。 - Eric Postpischil

0

整数常量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转换为该类型来获得任何无符号类型的最大值。您还可以使用<limits.h>中定义的宏:类型unsigned long的最大值为ULONG_MAX


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,