如何在printf函数在C工作? [英] How does the function printf works in 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
/等寄存器,三个程序的行为是这样的:
- 在第一个程序
的printf
查找%F
参数在XMM0注册,但因为我们是在节目的一开始,寄存器是干净的,主
已将A
到EAX
,从而XMM0
保持一个零值,这就是的printf
打印 - 在第二个程序
主
正确放置B
在XMM0
和的printf
需要从那里,打印正确的值 - 在第三个节目是由于这样的事实,
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:
- 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 andmain
has placeda
intoeax
, thusxmm0
holds a zero value, and this is whatprintf
prints - in the second program
main
correctly placesb
inxmm0
, andprintf
takes it from there, printing the correct value - in the third program due to the fact that
b
is printed first, thexmm0
register will hold this value, and sinceprintf
doesn't mess with the register, when it's called the second time it fetches again fromxmm0
that remained intact after the firstprintf
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屋!