使用%d打印无符号长整型

时间:2010-02-16 06:01:33

标签: c printf unsigned-long-long-int

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

unsigned long long int largestIntegerInC = 18446744073709551615LL;

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

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

是否因为溢出?

6 个答案:

答案 0 :(得分:4)

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

所以,初始化应该是:

unsigned long long int largestIntegerInC = 18446744073709551615ULL;

(请注意ULL。)由于largestIntegerInC的类型为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。或者你应该为你的变量存储-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类型的最大可能值。

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分:0)

你使用了一个带符号的32位数字的格式,所以得到了-1。 printf()无法在内部告诉您传入的数字有多大,因此它只是从varargs列表中提取前32位并将其用作要打印的值。由于你给出了一个带符号的格式,它会以那种方式打印出来,0xffffffff是-1的二进制补码表示。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

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

如果printf转换为%u,则会显示4294967295(32位)或18446744073709551615(64位)。

溢出是指值增加到不适合分配的存储空间的点。在这种情况下,值已分配就好了,但未完全检索