如何在printf函数在C工作? [英] How does the function printf works in C?

查看:128
本文介绍了如何在printf函数在C工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到我的时候测试printf函数的一个问题:

首先,我写code是这样的:

  INT主要(无效)
{
    所以char a ='A';
    输出(A =%F \\ N,一);
    返回0;
}

的输出是

然后我写code:

  INT主要(无效)
{
    浮动B ='A';
    的printf(B =%F \\ N,B);
    返回0;
}

的输出是

然后我写code:

  INT主要(无效)
{
    所以char a ='A';
    浮动B ='A';
    的printf(B =%F \\ N,B);
    输出(A =%F \\ N,一);
    返回0;
}

的输出是

所以我很困惑,为什么在第一个程序 A = 0.000000 ,并在第三个节目 A = 97.000000 ?结果
如何功能的printf()工作?结果
如何符号%F %d个工作?


解决方案

更新:在此做一些更多的研究后,似乎浮子之间的差异 INT 内存重新presentations是不负责的三个程序的行为的人。

我看了对象code第三方案,我发现了古怪行为的原因:浮点参数发送到其他注册表/堆栈不是整数的。和的printf 依赖于并期待它们在比那些的printf 的被叫方(即<$其他位置C $ C>主法)放置参数。

这里的第三个方案中的有关拆卸(对于x86_64体系):

  0000000100000f18 leaq 0x71(%RIP),%RDI ##的文字池:B =%F \\ N
0000000100000f1f MOVSD 0x61(%RIP),%## XMM0 b变量提供被发送到XMM0
0000000100000f27 MOVL $为0x0,-0x4(RBP%)
0000000100000f2e MOVB $ 0x61,-0x5(RBP%)##一个变量被放置在被调用堆栈上
0000000100000f32 MOVSD%XMM0,-0x10(RBP%)
0000000100000f37 MOVSD -0x10(RBP%),%XMM0
0000000100000f3c MOVB $为0x1,%人
0000000100000f3e callq 0x100000f66 ##符号存根:_printf
0000000100000f43 leaq 0x4e(%RIP),%RDI ##的文字池:一个=%F \\ N
0000000100000f4a movsbl -0x5(RBP%),ESI%
0000000100000f4e MOVL%EAX,-0x14(RBP%)
0000000100000f51 MOVB $为0x0,%人
0000000100000f53 callq 0x100000f66 ##符号存根:_printf

的printf 依赖于此,它假定被叫方已经把%F 在<$ C $参数C> XMM0 / 将xmm1 /等寄存器,三个程序的行为是这样的:


  1. 在第一个程序的printf 查找%F 参数在XMM0注册,但因为我们是在节目的一开始,寄存器是干净的,已将 A EAX ,从而 XMM0 保持一个零值,这就是的printf 打印

  2. 在第二个程序正确放置 B XMM0 的printf 需要从那里,打印正确的值

  3. 在第三个节目是由于这样的事实, B 第一次印刷,在 XMM0 寄存器将把此值,自的printf 不乱用寄存器,当它被称为从 XMM0 重新获取其第二次保留在第一的printf 电话后完好无损。

所以,它是所有关于在哪里整数和浮点数正在由主叫方和从那里被调用者试图捡起来发送来电者/被调用约定。





原始响应:在第一个程序,你要打印一个浮动,但你传递一个int(char是一个较小的INT)。由于这样的事实:整型和浮点型具有不同的二进制重新presentations中,int 97(对应于字符A)对应于一个非常小的浮:1.36E-43,即获取打印为零

下面是97二进制重新presentation(编译器调用函数时展开任何1字节字符为4个字节的参数)结果
00000000 00000000 00000000 01100001

IEEE 754 是二进制重新$ P的标准格式浮点/双精度数$ psentations,可以与在线转换这里玩,你可以看到同样的二进制数如何为不同的值时,其内部preTED为int或为float。

I met a problem when I test the function printf:

First I write code like this:

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

The output is

And then I write code:

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

The output is

And then I write code:

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

The output is

So I am confused why in the first program a = 0.000000 and in the third program a = 97.000000?
How does the function printf() work?
How does the symbol %f, %d work?

解决方案

Update: after doing some more research on this, it seems that the differences between the float and int memory representations are not the ones responsible for the behaviour of the three programs.

I've looked at the object code for the third program and I found the cause of the odd behaviour: the floating point arguments are sent to other registry/stack than the integer ones. And printf relies on that and looks them up in other locations than the ones the callee of printf (i.e. the main method) places the arguments.

Here's the relevant disassembly of the the third program (for an x86_64 architecture):

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

And printf relies on this, it assumes the callee has placed %f arguments in the xmm0/xmm1/etc registers, and the behaviour of the three programs is like this:

  1. in the first program printf looks for the %f argument in the xmm0 register, however as we're at the start of the program, the register is clean and main has placed a into eax, thus xmm0 holds a zero value, and this is what printf prints
  2. in the second program main correctly places b in xmm0, and printf takes it from there, printing the correct value
  3. in the third program due to the fact that b is printed first, the xmm0 register will hold this value, and since printf doesn't mess with the register, when it's called the second time it fetches again from xmm0 that remained intact after the first printf call.

So it's all about caller/callee conventions on where integers and floats are being send by the caller and from where the callee tries to pick them up.


Original response: In the first program you are trying to print a float, but you pass an int (char is a smaller int). Due to the fact that ints and floats have different binary representations, the int 97 (corresponding to the character 'a') corresponds to a very small float: 1.36E-43, that gets printed as zero.

Here is the binary representation of 97 (the compiler expands any 1-byte char to a 4-byte argument when calling a function)
00000000 00000000 00000000 01100001

IEEE 754 is the standard format for binary representations of float/double numbers, you can play with an online converter here, and you can see how the same binary number has different values when its interpreted as an int or as a float.

这篇关于如何在printf函数在C工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆