printf函数如何在C中运行?

时间:2015-04-16 04:52:44

标签: c

我在测试printf函数时遇到了一个问题:

首先我写这样的代码:

int main(void)
{
    char a = 'a';
    printf("a = %f\n", a);
    return 0;
}

输出

enter image description here

然后我写代码:

int main(void)
{
    float b = 'a';
    printf("b = %f\n", b);
    return 0;
}

输出

enter image description here

然后我写代码:

int main(void)
{
    char a = 'a';
    float b = 'a';
    printf("b = %f\n", b);
    printf("a = %f\n", a);
    return 0;
}

输出

enter image description here

所以我很困惑为什么在第一个程序a = 0.000000和第三个程序a = 97.000000? 函数printf()如何工作?
符号%f%d如何运作?

6 个答案:

答案 0 :(得分:6)

更新:在对此进行更多研究之后,似乎floatint内存表示之间的差异不是负责该行为的人三个节目。

我查看了第三个程序的目标代码,我发现了奇怪行为的原因:浮点参数被发送到其他注册表/堆栈而不是整数。并且printf依赖于它并在printf的被调用者(即main方法)放置参数的位置以外的其他位置查找它们。

这是第三个程序的相关反汇编(对于x86_64架构):

0000000100000f18    leaq    0x71(%rip), %rdi        ## literal pool for: "b = %f\n"
0000000100000f1f    movsd   0x61(%rip), %xmm0      ## b variable gets sent to xmm0
0000000100000f27    movl    $0x0, -0x4(%rbp)
0000000100000f2e    movb    $0x61, -0x5(%rbp)      ## a variable gets placed on the callee stack
0000000100000f32    movsd   %xmm0, -0x10(%rbp)
0000000100000f37    movsd   -0x10(%rbp), %xmm0
0000000100000f3c    movb    $0x1, %al
0000000100000f3e    callq   0x100000f66             ## symbol stub for: _printf
0000000100000f43    leaq    0x4e(%rip), %rdi        ## literal pool for: "a = %f\n"
0000000100000f4a    movsbl  -0x5(%rbp), %esi
0000000100000f4e    movl    %eax, -0x14(%rbp)
0000000100000f51    movb    $0x0, %al
0000000100000f53    callq   0x100000f66             ## symbol stub for: _printf

并且printf依赖于此,它假定被调用者在%f / xmm0 / etc寄存器中放置了xmm1个参数,并且这三个程序的行为是像这样:

    第一个程序printf中的
  1. 在xmm0寄存器中查找%f参数,但是当我们在程序开始时,寄存器是干净的main已将a放入eax,因此xmm0保留零值,这就是printf打印
  2. 第二个程序main中的
  3. 正确地将b放入xmm0printf从那里取出,打印正确的值
  4. 在第三个程序中,由于首先打印bxmm0寄存器将保留此值,并且由于printf不会使寄存器混乱,当它第二次从第xmm0次呼叫后保持原封不动的printf再次呼叫时被呼叫。
  5. 所有关于调用者/被调用者的约定都是关于调用者发送整数和浮点数以及被调用者尝试拾取它们的地方。


    原始回复:在第一个程序中,您尝试打印浮点数,但是传递了一个int(char是一个较小的int)。由于int和浮点数具有不同的二进制表示,int 97(对应于字符' a')对应于一个非常小的浮点数:1.36E-43,它被打印为零。

    这是97的二进制表示(编译器在调用函数时将任何1字节的char扩展为4字节的参数)
    00000000 00000000 00000000 01100001

    IEEE 754是浮点数/双数字的二进制表示的标准格式,您可以使用在线转换器here,并且可以看到相同的二进制数在解释为时具有不同的值一个int或一个浮点数。

答案 1 :(得分:5)

这里的%f表示浮动的替换标记。

为了替换字符,您需要%c

Here is a list告诉您每种类型的适当替换标记是什么。

答案 2 :(得分:1)

%f适用于Float  字符%c

The 97 which you have got is the ASCII value for 'a'

答案 3 :(得分:1)

根据最新的' C'标准它是一种未定义的行为。检查c标准草案的7.21.6.1第9页。

  

如果转换规范无效,则行为为   undefined.282)如果任何参数不是正确的类型   相应的转换规范,行为未定义。

因此,当事物被认为具有未定义的行为时,任何事情都是可能的,并且行为可能因编译器而异。 ' C'让你用斧头剪你的脚趾,但你不应该这样做。

答案 4 :(得分:0)

%f用于浮动。您必须使用%c作为字符。

如果您使用

    printf("a = %c\n", a);

你会得到这个角色。

因此,如果您将第一个代码更改为

int main(void)
{
    char a = 'a';
    printf("a = %c\n", a);
    return 0;
}

您将获得输出

a

答案 5 :(得分:0)

之间的区别
printf("%f\n, 97.0);

printf("%c\n, 'a');

printf 函数根据您给出的%X从堆栈中读取其参数,并解释它们(用于显示)。

对于%c,printf需要一个char作为参数,因此它将读取一个char(一个字节,但通常实际上是一个int,它依赖于实现)并显示它(如果)。

对于%f,printf需要一个float(大小以字节为单位sizeof(float),通常在gcc / Intel处理器上为4个字节)。

如果使用 gcc 进行编译,请使用-Wall选项,当%X格式与参数类型不匹配时,该选项会发出警告。