[英]C : Printing big numbers
请考虑以下事项:
#include <stdio.h>
main() {
unsigned long long verybig = 285212672;
printf("Without variable : %llu\n", 285212672);
printf("With variable : %llu", verybig);
}
这是上述程序的输出:
Without variable : 18035667472744448
With variable : 285212672
从上面可以看到,当printf
作为常量传递时,它会输出一些巨大的错误数字,但是当该值首次存储在变量中时, printf
输出正确的数字。
这背后的原因是什么?
试试285212672ULL
; 如果你在没有后缀的情况下编写它,你会发现编译器将它视为常规整数。 它在变量中工作的原因是因为整数在赋值中被强制转换为unsigned long long
,因此传递给printf()
的值是正确的类型。
在你问之前,不,编译器可能不够智能,无法从printf()
格式字符串中的"%llu
”中找出它。 这是一个不同的抽象层次。 编译器负责语言语法, printf()
语义不是语法的一部分,它是一个运行时库函数(除了它包含在标准中之外,与你自己的函数没有什么不同)。
对于32位int和64位unsigned long long系统,请考虑以下代码:
#include <stdio.h>
int main (void) {
printf ("%llu\n",1,2);
printf ("%llu\n",1ULL,2);
return 0;
}
哪个输出:
8589934593
1
在第一种情况下,两个32位整数1和2被压入堆栈, printf()
将其解释为单个64位ULL值,2 x 2 32 + 1. 2
参数无意中包含在ULL值。
在第二个中,您实际上是推送64位1值和多余的32位整数2
,这将被忽略。
请注意,格式字符串与实际参数之间的“不合时宜”是一个坏主意。 就像是:
printf ("%llu %s %d\n", 0, "hello", 0);
可能会崩溃,因为%llu
将消耗32位"hello"
指针, %s
将尝试取消引用最后的0
参数。 下面的“图片”说明了这一点(我们假设单元格是32位,“hello”字符串存储在0xbf000000。
What you pass Stack frames What printf() uses
+------------+
0 | 0 | \
+------------+ > 64-bit value for %llu.
"hello" | 0xbf000000 | /
+------------+
0 | 0 | value for %s (likely core dump here).
+------------+
| ? | value for %d (could be anything).
+------------+
值得指出的是,有些编译器会针对这种情况提供有用的警告 - 例如,这就是GCC对您的代码所说的内容:
x.c: In function ‘main’:
x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’
285212672
是一个int
值。 printf
期望一个unsigned long long
,你传递一个int
。 因此,它会从堆栈中获取更多的字节,而不是传递实际值并打印垃圾。 当你将它放在一个unsigned long long
变量中,然后将它传递给函数之前,它将在赋值行中被提升为unsigned long long
,并将该值传递给printf
,它正常工作。
数据类型只是一种解释内存位置内容的方法。
在第一种情况下,常量值作为int存储在只读存储器位置中,printf尝试将该地址解释为8字节位置,因为它被指示存储的值在其打印垃圾值的过程中很长。
在第二种情况下,printf尝试将长long值解释为8个字节,并打印出预期的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.