printf的问题,非常奇怪,百思不得其解

widowss 2005-12-29 11:21:11
#include <stdio.h>
main()
{
int i=5;
printf("%d",5.0);
printf("%d",i);
printf("%f",5.0);
}


为什么输出结果是0,5.00000。第一个0是怎么得来得,尝试自己写一个类似printf的程序,没有成功,谁能解释一下啊。
...全文
574 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
plane10598 2005-12-31
  • 打赏
  • 举报
回复
没试过!也很奇怪
Renkey 2005-12-30
  • 打赏
  • 举报
回复
期待,学习下。
Grubby_c 2005-12-30
  • 打赏
  • 举报
回复
关注 cky41(有点魄力)
关注高手
bombwang 2005-12-30
  • 打赏
  • 举报
回复
learning
megaboy 2005-12-29
  • 打赏
  • 举报
回复
printf由于是可变参数函数,它是不自动进行类型转换的,而是根据格式说明符的说明去读取参数的内容,printf("%d", 5.0)这样是把5.0这个浮点数的浮点内存映象当作整数来读取,当然不是你所预期的结果。
goodluckyxl 2005-12-29
  • 打赏
  • 举报
回复
0040102F push 40140000h
00401034 push 0 //是这样的push了0
00401036 push offset string "%d" (00424020)
0040103B call printf (004010a0)
00401040 add esp,0Ch

改成5就push 5 了
goodluckyxl 2005-12-29
  • 打赏
  • 举报
回复
不会吧 有这事
afei_xyd 2005-12-29
  • 打赏
  • 举报
回复
这个函数是可变参数的函数,其实现不看后面参数有多少个的,只看第一个参数,根据数据格式来输出字符串,当后面的数据类型不一致是不进行数据类型转换的,可能是默认值吧。关于自己实现printf()函数,我见过现在找不到了。可能是标准c 里吧,你自己找找
sankt 2005-12-29
  • 打赏
  • 举报
回复
学习
xombat 2005-12-29
  • 打赏
  • 举报
回复
说的很清楚了
widowss 2005-12-29
  • 打赏
  • 举报
回复
同期待
iwantfat 2005-12-29
  • 打赏
  • 举报
回复
关注
kangji 2005-12-29
  • 打赏
  • 举报
回复
有意思
cky41 2005-12-29
  • 打赏
  • 举报
回复
这个问题比较复杂
先说我是在GNU/Linux平台上测试的。


printf ("%d\n", 5.0)
编译器看到带有小数点的十进制数,那么会将其默认为double类型(而不是float),double类型在一般的机子里都占有8个字节(64位)。但是%d对应int类型,int类型在一般的机子里占有4个字节(32位,也就是long),所以%d只读传给printf的低4字节的值,而这4字节刚好是0,所以printf ("%d\n", 5.0)打印出0。前面goodluckyxl用汇编表示的更清楚。

至于为什么这低4字节刚好是0,这跟浮点数在计算机中的实际存储有关。int 5在内存中就存为0x00000005,但是float 5或者double 5在内存中决不是这么简单的数,它会高字节低字节的存不同的信息,好像是指数尾数之类的,关于这部分我就不清楚了。

所以如果你这样
printf ("%d\n", 5.1),那么我相信低4字节决不是0,这回一定能打印出非0整数。

或者这样
printf ("%lld", 5.0),%lld会转换8字节的值为整数,所以打印出的值一定不是0,但因为上面说得浮点数的存储不同于简单的整数,所以打印出的数可能很怪,一般都很大。

上面这一部分应该能解决楼主的问题了。
下面的是需要大家讨论的,我是在解决楼主问题的时候发现的。


说到这,有人可能会想既然%d对应int,也就是转换4字节的值,那么如果我这样
printf ("%d\n", (float)5.0)
是不是就可以打印出非0值了,因为float是4字节,所以5.0在内存中这回是占用了4字节,所以%d应该能够转换出非0值了。我也是这样想的,并且很有信心会出现预期的结果。
不过。。。
还是打印0 (我是在GNU/Linux平台上测试的)
就算我这样
float i = 5.0;
printf ("%d\n", i);
也还是打印0

我看了printf ("%d\n", (float)5.0)最后压入栈的情况是这样的
movl $0, %eax
movl $1075052544, %edx
pushl %edx
pushl %eax
pushl $.LC9 ; (float) 5.0
call printf
这和printf ("%d\n", 5.0)或者printf ("%d\n", (double)5.0)的情形完全一样

我又看了printf ("%d\n", (float)5.1)的情形,他和printf ("%d\n", 5.1)的情形稍有不同——eax的值差一点——其他都完全一样。

其实不管是5.0还是5.1我发现call printf前都有三次压栈,除去pushl $.LC9是将"%d\n"压入栈,其他两个pushl %edx和push %eax实际就是代入参数5.0或者5.1的值。这两次一共压了8字节,可是我以(float) 5.0作为参数代入时,应该只有4字节啊。所以我怀疑不论是float还是double在作为参数代入printf时,都转换成了8字节(代入普通函数时好像是直接压入栈)。所以打印出的5.0还是为0,因为低4字节凑巧还是0。

当我这样时
float i = 5.0;
printf ("%d\n", i);
我看了汇编代码,情况更加复杂
movl $0x40a00000, %eax ; 0x40a00000 就是 5.0
movl %eax, -4(%ebp)
subl $4, %esp
flds -4(%ebp)
leal -8(%esp), %esp
fstpl (%esp)
pushl $.LC1 ; 将"%d\n"压入栈
call printf
这好像和什么FPU堆栈有关,好像是将所有浮点数转换成10字节,然后代入。

如果我这样
float i = 5.0;
foo (i); // foo的原型是void foo (float)
汇编为
movl $0x40a00000, %eax
movl %eax, -4(%ebp)
pushl -4(%ebp)
call foo
好象是把4字节扩展成8字节,然后压栈。

如果我这样
foo (5.0);
汇编为
movl $0x40a00000, %eax
pushl %eax
call foo
直接压栈。

关于浮点数,gcc编译器为什么会做这样的处理,还有待高人指点迷津。

weping 2005-12-29
  • 打赏
  • 举报
回复
产生不可预料的后果了?
jianwang_yz 2005-12-29
  • 打赏
  • 举报
回复
printf("%d\n", 5.0); //结果是0
printf("%d\n",i);
printf("%d\n",5.0); //结果是5.0000000
printf("%f\n",5); //结果是0.0000

这应该跟库里printf函数的编写有关,是不是得考虑浮点数在内存中的存储问题,记得浮点数载内存中是以定点数形式存放的,即存储一个相应整数,然后再存储小数点的位置,这样当printf函数按%d形式读取时,它就不会考虑形式了,将其当整型读取了!

fiftymetre 2005-12-29
  • 打赏
  • 举报
回复
printf("%f",5.0)你这样呢
waterczh 2005-12-29
  • 打赏
  • 举报
回复
printf("%f",5)也是0,能不能再解释详细一点呢

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧