我在测试printf函数时遇到了一个问题:
首先我写这样的代码:
int main(void)
{
char a = 'a';
printf("a = %f\n", a);
return 0;
}
输出
然后我写代码:
int main(void)
{
float b = 'a';
printf("b = %f\n", b);
return 0;
}
输出
然后我写代码:
int main(void)
{
char a = 'a';
float b = 'a';
printf("b = %f\n", b);
printf("a = %f\n", a);
return 0;
}
输出
所以我很困惑为什么在第一个程序a
= 0.000000
和第三个程序a
= 97.000000
?
函数printf()
如何工作?
符号%f
,%d
如何运作?
答案 0 :(得分:6)
更新:在对此进行更多研究之后,似乎float
和int
内存表示之间的差异不是负责该行为的人三个节目。
我查看了第三个程序的目标代码,我发现了奇怪行为的原因:浮点参数被发送到其他注册表/堆栈而不是整数。并且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
中的%f
参数,但是当我们在程序开始时,寄存器是干净的main
已将a
放入eax
,因此xmm0
保留零值,这就是printf
打印main
中的b
放入xmm0
,printf
从那里取出,打印正确的值b
,xmm0
寄存器将保留此值,并且由于printf
不会使寄存器混乱,当它第二次从第xmm0
次呼叫后保持原封不动的printf
再次呼叫时被呼叫。所有关于调用者/被调用者的约定都是关于调用者发送整数和浮点数以及被调用者尝试拾取它们的地方。
原始回复:在第一个程序中,您尝试打印浮点数,但是传递了一个int(char是一个较小的int)。由于int和浮点数具有不同的二进制表示,int 97(对应于字符' a')对应于一个非常小的浮点数:1.36E-43,它被打印为零。
这是97的二进制表示(编译器在调用函数时将任何1字节的char扩展为4字节的参数)
00000000 00000000 00000000 01100001
IEEE 754是浮点数/双数字的二进制表示的标准格式,您可以使用在线转换器here,并且可以看到相同的二进制数在解释为时具有不同的值一个int或一个浮点数。
答案 1 :(得分:5)
答案 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
格式与参数类型不匹配时,该选项会发出警告。