为什么在打印以下内容时会得到 -1?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
我知道我应该使用llu
而不是d
,但为什么我得到 -1 而不是 18446744073709551615LL?
是因为溢出吗?
为什么在打印以下内容时会得到 -1?
unsigned long long int largestIntegerInC = 18446744073709551615LL;
printf ("largestIntegerInC = %d\n", largestIntegerInC);
我知道我应该使用llu
而不是d
,但为什么我得到 -1 而不是 18446744073709551615LL?
是因为溢出吗?
在 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;
}
在上面的程序中,largestIntegerInC
和next
都包含类型的最大可能值unsigned long long int
。
这是因为您传递了一个所有位都设置为 1 的数字。当解释为二进制补码有符号数字时,结果为 -1。在这种情况下,它可能只查看其中的 32 个而不是全部 64 个,但这并没有真正的区别。
在二进制补码算术中,有符号值 -1 与最大无符号值相同。
考虑二进制补码中负数的位模式(我使用的是 8 位整数,但无论大小如何,该模式都适用):
0 - 0x00
-1 - 0xFF
-2 - 0xFE
-3 - 0xFD
因此,您可以看到负 1 具有全 1 的位模式,这也是最大无符号值的位模式。
您使用了带符号的 32 位数字的格式,因此得到 -1。 printf()
无法在内部告诉您传入的数字有多大,因此它只是从可变参数列表中提取前 32 位并将它们用作要打印的值。由于您提供了签名格式,因此它会以这种方式打印,并且 0xffffffff 是 -1 的二进制补码表示。
您可以(应该)在编译器警告中看到原因。如果没有,请尝试设置最高警告级别。使用 VS 时,我收到此警告:警告 C4245:“正在初始化”:从“__int64”转换为“无符号__int64”,有符号/无符号不匹配。
不,没有溢出。这是因为它没有打印整个值:
18446744073709551615 与 0xFFFFFFFFFFFFFFFF 相同。当printf %d
处理它时,它只获取 32 位(如果是 64 位 CPU,则为 64 位)进行转换,这些是有符号值 -1。
如果是printf
转换%u
,它将显示 4294967295(32 位)或 18446744073709551615(64 位)。
溢出是指一个值增加到不适合分配的存储空间的程度。在这种情况下,该值被分配得很好,但没有被完全检索。