printf函数如何处理%f规范?

Jee*_*tel 0 c floating-point string-formatting

我有几个程序,其输出我无法理解:

计划1

#include <stdio.h>
int main(void)
{
  int i=1;
  float k=2;
  printf("k is %f \n",k);
  printf("i is %f \n",i);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出位于http://codepad.org/ZoYsP6dc

k is 2.000000 
i is 2.000000
Run Code Online (Sandbox Code Playgroud)

计划2

再举一个例子

#include <stdio.h>

int main(void)
{
  char i='a';
  int a=5;
  printf("i is %d \n",i);  // here %d type cast char value in int
  printf("a is %f \n",a);  // hete %f dont typecast float value
  printf("a is %f \n",(float)a);  // if we write (float) with %f then it works
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出位于http://codepad.org/XkZVRg64

i is 97 
a is 2.168831 
a is 5.000000 
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么我显示输出?

Jon*_*ler 8

首先,使用任何可变函数,例如printf(),类型的所有整数值int都被传递为int(或unsigned int在某些情况下在某些平台上),并且所有float值都作为传递double.(float)a因此,你第二次投了double.

其次,printf()它本身会带你到你的话.如果你把垃圾送进去,就会把垃圾扔掉.更准确地说,如果你传递一个你告诉printf()期望的整数double,printf()将尝试double从参数列表中读取a .接下来发生的是未定义的行为.

一些编译器 - 尤其是GCC - 将报告文字字符串格式与相应参数列表之间的类型不匹配.如果您的常规编译器不进行该分析,请考虑使用一个编译器 - 至少对于初步编译来解决编译错误.


Max*_*kin 8

您可能正在64位x86架构上运行此应用程序.在此体系结构中,浮点参数在XMM寄存器中传递,而整数参数在通用寄存器中传递.请参阅System V AMD64 ABI约定.

因为%f需要一个浮点值:

printf("i is %f \n",i);
Run Code Online (Sandbox Code Playgroud)

打印XMM0寄存器中的值,该值恰好是k先前分配的值,而不是i在RSI寄存器中传递.程序集看起来像这样:

movl    $.LC1, %edi       # "k is %f \n"
movsd   .LC0(%rip), %xmm0 # float k = 2
call    printf
movl    $1, %esi          # int i = 1
movl    $.LC2, %edi       # "i is %f \n"
call    printf            # prints xmm0 because of %f, not esi
Run Code Online (Sandbox Code Playgroud)

如果您重新排序这样的分配:

int i = 1;
printf("i is %f \n",i);

float k = 2;
printf("k is %f \n",k);
Run Code Online (Sandbox Code Playgroud)

它打印:

i is 0.000000 
k is 2.000000 
Run Code Online (Sandbox Code Playgroud)

因为XMM0寄存器恰好具有值0.

[更新] 它也可以在32位x86上重现.在这个平台printf()基本上是铸造int*double*,然后读取double.让我们修改示例以便于查看:

int main() {
    float k = 2;
    int i = -1;
    printf("k is %f \n",k);
    printf("i is %f \n",i,i);
}
Run Code Online (Sandbox Code Playgroud)

64位输出:

k is 2.000000 
i is 2.000000 
Run Code Online (Sandbox Code Playgroud)

32位输出:

k is 2.000000 
i is -nan 
Run Code Online (Sandbox Code Playgroud)

也就是说,int值为-1的2 s看起来像是double0xffffffffffffffff,它是一个NaN值.

  • +1用于确定和解释机制. (2认同)