为什么在打印以下内容时会得到-1?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
我知道我应该使用llu
代替d
,但为什么我会得到-1而不是18446744073709551615LL?
是否因为溢出?
答案 0 :(得分:4)
在C(99),LLONG_MAX
中,long long int
类型的最大值保证至少为9223372036854775807
。 unsigned 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;
}
在上述计划中,largestIntegerInC
和next
都包含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位)。
溢出是指值增加到不适合分配的存储空间的点。在这种情况下,值已分配就好了,但未完全检索。