请教一个关于C语言printf和指针的问题

先看下面的代码:
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
main(){
float f,*p;
f=10.1;
p=&f;
printf("%p %d %f\n",p,p,*p);
/*以下三行在我的电脑上输出为 "10.1 0.0" */
printf("%f %f\n",p,*p);
printf("%f %f\n",*p,p);
printf("%f %f\n",p,p);
printf("#####################\n");
/*以下三行在我的电脑上输出为 "10.1 10.1" */
printf("%f %f\n",*p,*p);
printf("%f %f\n",*p,p);
printf("%f %f\n",p,p);
}

printf("%f",p) (首先说明,这种输出在逻辑上是不通的)为什么有时候能把输出正确的数值,有时候不可以?
以下是在LINUX下用gcc编译后输出的结果
其输出:
0x7fffc3b23e24 -1011728860 10.100000 //printf("%p %d %f\n",p,p,*p);
10.100000 0.000000 //printf("%f %f\n",p,*p);
10.100000 0.000000 //printf("%f %f\n",*p,p);
10.100000 0.000000 //printf("%f %f\n",p,p);
#####################
10.100000 10.100000 //printf("%f %f\n",*p,*p);
10.100000 10.100000 //printf("%f %f\n",*p,p);
10.100000 10.100000 //printf("%f %f\n",p,p);

先前说得正确结果和错误结果,容易使人误解成10.1和10.099996的差别,我的本意不是说这个。那我再说得具体点,就是
printf("%f\n",p) 和printf("%f\n",*p) 这个格式,为何在不同的语句输出后会有差别?
望高人指点

有意思的发现。要说是printf的bug不如说是C的缺陷。
首先说的基本依据:1、C压栈顺序是从右到左,也就是前面的参数位置比较低;2、C语言规定所有float参数在传递过程全部转化成double,这大概是历史原因吧;3、关于可变参数,编译器不进行检查,也没有能力检查,它只是把参数全数压栈,正确与否全赖程序员了,还是那句话,C认为程序员永远是对的;4、既而,像printf、scanf等等函数根本无法知道究竟传给它的是神马参数,也就无从谈检查格式串与参数是否匹配,所以尽管printf("%f\n",p)不合逻辑,甚至printf("%f%f%f%f\n",p)除了看代码的人,我目前只知道的是lint程序能够做这种检查;5、GCC的堆栈结构比较特别,GCC函数在入口处直接开出足够的栈帧空间,因此只要执行点在函数里,实际上堆栈指针位置就始终不动,Turbo c不是这样的,VC没看过不知道,我一直不知道GCC这样是优点还是缺点;6、要看是神马机子,32位机子上指针4字节,double浮点数8字节(是指针的2倍),符号指数等重要的部分在高字节,低字节是精度部分。

进入正题,这段代码的特点就是全部是printf调用,所以在GCC中编译相当于后面调用的参数会重叠覆盖在前面调用的参数的位置上。每次printf期望的参数是(除了前面的格式串)两个double共16字节,第二次调用给足了16字节,后续3次调用实际上没有给足数量的字节数,所以printf用了前面遗留的没有被覆盖掉的浮点数 *p 的高4字节,而低4字节给覆盖掉了,结果影响了小数点后面靠后的一点精度改变了。所以关键还是在前一次调用留下的有些痕迹没有被覆盖掉,后一次调用给错参数时printf重用了这些痕迹。LZ可以试试在printf语句之间插入一些至少含有3个double参数的其他函数调用,就可以看出效果来了。
温馨提示:答案为网友推荐,仅供参考
第1个回答  2011-12-31
printf("%f",p)为什么有时候能把输出正确的数值,有时候不可以?
- p在32位机器32位编译器的平台上是32位整数,强制按照浮点类型输出时会因不同编译器对printf函数内部代码的不同而造成结果异常。
- 这既不是因为printf内部对浮点数精度处理有误,也不是bug。

如何让它显示出正确的数值?
- 在格式输出之前对p进行强制类型转换,比如:printf("%f", (double)(unsigned int)p);
- 在无所谓精度的前提下,double可换成float,unsigned int可换为int/short/long/unsigned short/unsigned long等等。
- 理论上而言,后一层类型转换应当是由编译器隐式执行的。但如果仅使用一层double/float类型转换,gcc会报告“应当使用浮点类型时使用了指针类型”(比如程序员漏写了*)的错误。
第2个回答  2011-12-31
printf("%f",p) (首先说明,这种输出在逻辑上是不通的) ----------的确,这样书是错误的用法!!
我也测试了一下,如果这样写:
printf("%f %f\n",*p,*p); //10.09996 10.099996
printf("%f %f\n",*p,p); //10.09996 10.09996 ERROR1
printf("%f %f\n",p,p); //0.000 OK 10.09996 ERROR2
printf("%f\n",p); //0.000 这是正常的输出
printf("%f\n",p); //0.000 这是正常的输出
因此,我觉得这是printf函数的BUG,究竟为什么 等高人吧
既然知道什么是正确的用法,就采用正确的用法好了!!!学会正确使用C函数是很必要的!本回答被网友采纳
第3个回答  2011-12-31
输出数值的误差主要是因为计算机采用二进制,对浮点数的处理精度不够造成的,属于正常的现象,可以通过控制输出小数位数来显示正确的结果。
printf("%f\n",p);想要输出的是变量在内存中的地址,但是地址是整型的,%f不会对类型进行转换,输出的结果一般都是不对的。
相似回答
大家正在搜