当printf中使用%d时,float变量会发生什么?

Gol*_*lum 16 c floating-point printf

我想通过阅读来学习C C程序设计语言,第2版.我有一些编程经验,但没有C.

我目前在第1章.我有以下代码:


  float f;
  for (f = 0.0; f <= 3; f += 1.1)
      printf("A: %3f B: %6.2f\n", f, f + 0.15);
Run Code Online (Sandbox Code Playgroud)

它打印输出:

A: 0.000000 B:   0.15
A: 1.100000 B:   1.25
A: 2.200000 B:   2.35
Run Code Online (Sandbox Code Playgroud)

看起来很好.


现在我按如下方式更改printf:

printf("A: %3d B: %6.2f\n", f, f + 0.15);
Run Code Online (Sandbox Code Playgroud)

新的输出是

A:   0 B:   0.00
A: -1610612736 B:   0.00
A: -1610612736 B: -625777476808257557292155887552002761191109083510753486844893290688350183831589633800863219712.00
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?我希望浮点数转换为int,因为我使用了%d,但事实并非如此.另外,为什么价值B也出错?这里发生了什么事?

Jon*_*ler 17

你打电话的时候:

printf("A: %3d B: %6.2f\n", f, f + 0.15);
Run Code Online (Sandbox Code Playgroud)

C自动将float值转换为double(它是在调用带有可变参数的函数时进行的标准转换,例如int printf(const char *fmt, ...);).为了论证,我们假设它sizeof(int)是4并且sizeof(double)是8(有例外,但它们很少而且很远).

因此,调用已将指针推入堆栈,加上一个8字节的双精度数f,另一个8字节的双精度数f + 0.15.当它处理格式字符串时,%d告诉printf()int在格式字符串后将4字节压入堆栈.由于这不是你所做的,你已经调用了未定义的行为; 根据C标准,接下来发生的一切都没问题.

然而,最可能的实现轻快地读取4个字节并将它们打印成好像它们int(它相信你说实话).然后它遇到了%6.2f格式; 它将从堆栈中读取8个字节作为a double.有一个外部的可能性,这会导致内存错误导致访问错位(它需要64位机器,要求double在8字节边界上对齐,例如SPARC),否则它将读取4个字节f和从4个字节开始f + 0.15,将它们放在一起以创建一些非常意外的double值 - 如您的示例所示.

  • 在大量相当合理的假设下,是的。该行为是严格未定义的,但在实践中,是的。 (2认同)

Ste*_*owe 8

无论你告诉它,Printf都会将你指向的记忆对待.没有转换.它将表示float的内存视为int.因为两者的存储方式不同,所以你得到的本质上是一个随机数.

如果要将float输出为整数,则应首先将其转换为:

printf("A: %3d B: %6.2f\n", (int)f, f + 0.15); 
Run Code Online (Sandbox Code Playgroud)