答案对人有帮助,有参考价值
6
答案没帮助,是错误的答案,答非所问
采纳
pezy 2.5k
2015年02月04日 回答 · 2015年02月04日 更新
首先,float
在这里会自动转换为double
, 为了方便理解,我们在这里假设 sizeof(int)
是 4, sizeof(double)
是 8.
然后,针对这一句:
printf ("%d \n%f \n%d \n%f \n" , a, a, b, b);
依照 %d\n%f\n%d\n%f
去读,栈里面的数据 以及读取的指针 将呈现如下形式:
(int )a_>| |4
|_2.5 _|4
(float )a_>| |4
(int )b_>|_2.5 _|4
|__2__|4
(float )b_>|__2__|4
所以呢,第一个 %d
读到的实际是 2.5 的前 4 byte。 紧接着第一个 %f
读的是 2.5 的后 4 byte, 加上下一个 2.5 的前 4 byte. 同理,第二个 %d
读到的是 2.5 的后 4 byte, 而第二个 %f
读到的是两个 2.
好吧,是不是有点乱。C 语言中是如何定义这种在输出里"类型乱读"的现象呢?
7.21.6 Formatted input/output functions:
If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined .
如标准所言,这是 UB (未定义行为),所以至于它为何会输出你截图里的那四个数,可以不必深究。不同编译器的结果也不尽相同。
补充
上面分析了原理,那么也可以顺带分析一下你的第二句为什么可以输出你想要的。
依照 %f\n%d\n%f\n%d
去读,栈里面的数据 以及读取的指针 将呈现如下形式:
| |4
(float )a_>|_2.5 _|4 _ _ _ _ _ _ correct!
(int )a_>| |4
|_2.5 _|4
(float )b_>|__2__|4
(int )b_>|__2__|4 _ _ _ _ _ _ correct!
运气不错,牛头正好对上了马嘴,你得到了你想要的输出。。。
不要侥幸,老老实实的按类型去 printf
, 才是初学者正确的态度。
答案对人有帮助,有参考价值
3
答案没帮助,是错误的答案,答非所问
RobinTang 2.2k
2015年02月04日 回答 · 2015年02月04日 更新
你留意一下标准库里面的stdarg.h文件,它里面定义了几个宏,分别是va_start 、va_arg 、va_end ,这几个宏都是用来做不定参数传递的。特别注意的是va_arg在获取传递进来的参数时依赖它的第二个参数(t,类型),如果类型不对应的话会导致整个不定参数的传递没法正常的解析(错位)。prinft依赖于vsprintf,而vsprintf的实现依赖这几个宏,它会根据格式字符串(也就是那些%d %f ...)来调用va_arg,如果你的格式字符串和你后面的不定参数没有正确对应起来,那么也有可能在获取不定参数时出现错位(而你第二个输出对了两次也是因为错位了两次之后刚好又对上了的缘故)。
还有float(32位)参数在传递printf的时候会自动转换成double(64位)(这也是%f和%lf没区别的缘故),int参数占32位。
有了上面的铺垫之后,下面来解释你的这个问题。 环境:Windows XP 32, gcc version 3.4.0 (mingw special)
注:以下连续的十六进制数据从左到右对应于内存地址的由低到高
你传入的参数a,a,b,b ,对应十六进制的00 00 00 00 00 00 04 40 00 00 00 00 00 00 04 40 02 00 00 00 02 00 00 00 正确的分割应该是:
a : [00 00 00 00 00 00 04 40]
a : [00 00 00 00 00 00 04 40]
b : [02 00 00 00]
b : [02 00 00 00]
根据你的第一次输出的格式字符串,printf做了如下的分割:
%d : [00 00 00 00 ] int ,对应0x00000000 ,也就是0 了
%f : [00 00 04 40 00 00 00 00 ] double,0 .0 ,(看double的表示法)
%d : [00 00 04 40 ] int ,对应0x40040000 ,也就是10 进制的1074003968
%f : [02 00 00 00 02 00 00 00 ] double,0 .0
所以输出也就是:
0 0.000000 1074003968 0.000000
而根据你第二次输出的格式字符串,printf做了如下的分割:
%f : [00 00 00 00 00 00 04 40 ] double, 对应2.5
%d : [00 00 00 00 ] int 对应0
%f : [00 00 04 40 02 00 00 00 ] double,对应0
%d :[02 00 00 00 ] int 对应0
也就是你看到的:
2.500000 0 0.000000 2
至于为什么好几次double都变成0.0,你需要去看看IEEE的double表示标准。
简而言之就是:printf的在做输出的时候会根据格式字符串来获取传递进来的不定参数,而错误的格式字符串会导致不定参数的获取错误,从而导致输出错误。