2

为什么在打印以下内容时会得到 -1?

unsigned long long int largestIntegerInC = 18446744073709551615LL;

printf ("largestIntegerInC = %d\n", largestIntegerInC);

我知道我应该使用llu而不是d,但为什么我得到 -1 而不是 18446744073709551615LL?

是因为溢出吗?

4

6 回答 6

4

在 C (99) 中,保证类型LLONG_MAX的最大值long long int至少为9223372036854775807。an 的最大值unsigned long long int保证至少为18446744073709551615,即 2 64 -1 ( 0xffffffffffffffff)。

所以,初始化应该是:

unsigned long long int largestIntegerInC = 18446744073709551615ULL;

(注意ULL。)由于largestIntegerInC是 type unsigned long long int,您应该使用正确的格式说明符打印它,即"%llu"

$ cat test.c
#include <stdio.h>

int main(void)
{
    unsigned long long int largestIntegerInC = 18446744073709551615ULL;
    /* good */
    printf("%llu\n", largestIntegerInC);
    /* bad */
    printf("%d\n", largestIntegerInC);
    return 0;
}
$ gcc  -std=c99 -pedantic test.c
test.c: In function ‘main’:
test.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long unsigned int’

上面的第二个printf()是错误的,它可以打印任何东西。您正在使用"%d",这意味着printf()期待一个int,但得到一个unsigned long long int,这(很可能)与int. 您获得-1输出的原因是(运气不好),以及在您的机器上,数字使用二进制补码表示表示。


要看看这有多糟糕,让我们运行以下程序:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(int argc, char *argv[])
{
    const char *fmt;
    unsigned long long int x = ULLONG_MAX;
    unsigned long long int y = 42;
    int i = -1;
    if (argc != 2) {
        fprintf(stderr, "Need format string\n");
        return EXIT_FAILURE;
    }
    fmt = argv[1];
    printf(fmt, x, y, i);
    putchar('\n');
    return 0;
}

在我的 Macbook 上,运行程序"%d %d %d"给我-1 -1 42,在 Linux 机器上,相同格式的相同程序给我-1 42 -1。哎呀。


事实上,如果你想unsigned long long int在你的变量中存储最大的数字largestIntegerInC,你应该包含limits.h并使用ULLONG_MAX. 或者您应该将 assing 存储-1到您的变量中:

#include <limits.h>
#include <stdio.h>

int main(void)
{
    unsigned long long int largestIntegerInC = ULLONG_MAX;
    unsigned long long int next = -1;
    if (next == largestIntegerInC) puts("OK");
    return 0;
}

在上面的程序中,largestIntegerInCnext都包含类型的最大可能值unsigned long long int

于 2010-02-16T06:15:04.093 回答
3

这是因为您传递了一个所有位都设置为 1 的数字。当解释为二进制补码有符号数字时,结果为 -1。在这种情况下,它可能只查看其中的 32 个而不是全部 64 个,但这并没有真正的区别。

于 2010-02-16T06:04:10.220 回答
1

在二进制补码算术中,有符号值 -1 与最大无符号值相同。

考虑二进制补码中负数的位模式(我使用的是 8 位整数,但无论大小如何,该模式都适用):

 0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD

因此,您可以看到负 1 具有全 1 的位模式,这也是最大无符号值的位模式。

于 2010-02-16T06:03:11.190 回答
0

您使用了带符号的 32 位数字的格式,因此得到 -1。 printf()无法在内部告诉您传入的数字有多大,因此它只是从可变参数列表中提取前 32 位并将它们用作要打印的值。由于您提供了签名格式,因此它会以这种方式打印,并且 0xffffffff 是 -1 的二进制补码表示。

于 2010-02-16T06:03:30.963 回答
0

您可以(应该)在编译器警告中看到原因。如果没有,请尝试设置最高警告级别。使用 VS 时,我收到此警告:警告 C4245:“正在初始化”:从“__int64”转换为“无符号__int64”,有符号/无符号不匹配。

于 2010-02-16T06:12:02.197 回答
0

不,没有溢出。这是因为它没有打印整个值:

18446744073709551615 与 0xFFFFFFFFFFFFFFFF 相同。当printf %d处理它时,它只获取 32 位(如果是 64 位 CPU,则为 64 位)进行转换,这些是有符号值 -1。

如果是printf转换%u,它将显示 4294967295(32 位)或 18446744073709551615(64 位)。

溢出是指一个值增加到不适合分配的存储空间的程度。在这种情况下,该值被分配得很好,但没有被完全检索

于 2010-02-16T06:24:38.613 回答