Laz*_*zer 88 c c++ memory printf endianness
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它显示0
!! 怎么可能?是什么原因?
我故意%d
在printf
声明中加入研究行为printf
.
ken*_*ytm 238
那是因为%d
期待一个,int
但你提供了一个浮动.
使用%e
/ %f
/ %g
打印浮动.
关于为什么打印0:浮点数double
在发送之前转换为printf
.小端的双重表示的数字1234.5是
00 00 00 00 00 4A 93 40
Run Code Online (Sandbox Code Playgroud)
A %d
消耗32位整数,因此打印零.(作为测试,你printf("%d, %d\n", 1234.5f);
可以得到输出0, 1083394560
.)
至于为什么float
转换为double
printf的原型int printf(const char*, ...)
,从6.5.2.2/7,
函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数提升是在尾随参数上执行的.
从6.5.2.2/6开始,
如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有类型的参数
float
提升为double
.这些被称为默认参数促销.
(感谢Alok发现这一点.)
Alo*_*hal 45
从技术上讲是没有的 printf
,每个库实现了自己,因此,你的努力学习方法printf
做你正在做什么的行为是不会有太大的用处.您可能正在尝试研究printf
系统上的行为,如果是这样,您应该阅读文档,并查看源代码printf
是否适用于您的库.
例如,在我的Macbook上,我得到了1606416304
你的程序的输出.
话虽如此,当你传递一个float
变量函数时,float
它作为一个传递double
.所以,你的程序相当于声明a
为a double
.
要检查a的字节double
,你可以在SO上看到这个最近问题的答案.
我们这样做:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
unsigned char *p = (unsigned char *)&a;
size_t i;
printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
for (i=0; i < sizeof a; ++i)
printf("%02x ", p[i]);
putchar('\n');
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我运行上述程序时,我得到:
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
Run Code Online (Sandbox Code Playgroud)
因此,结果的前四个字节为double
0,这可能就是为什么你得到0
了你的printf
呼叫输出.
为了获得更有趣的结果,我们可以稍微改变一下程序:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我在Macbook上运行上述程序时,我得到:
42 1606416384
Run Code Online (Sandbox Code Playgroud)
在Linux机器上使用相同的程序,我得到:
0 1083394560
Run Code Online (Sandbox Code Playgroud)
Mar*_*ner 20
该%d
说明符告诉printf
期待一个整数.因此,浮点数的前四个(或两个,取决于平台)字节被解释为整数.如果它们恰好为零,则打印零
1234.5的二进制表示就像
1.00110100101 * 2^10 (exponent is decimal ...)
Run Code Online (Sandbox Code Playgroud)
使用C编译器float
实际上表示为IEEE754双值,字节将是(如果我没有错误)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
Run Code Online (Sandbox Code Playgroud)
在具有少量字节序的英特尔(x86)系统(即最先出现的最低有效字节)上,此字节序列被反转,因此前四个字节为零.那就是printf
打印出来的东西......
有关根据IEEE754的浮点表示,请参阅此Wikipedia文章.
因为您调用了未定义的行为:您通过向其说明其参数类型违反了printf()方法的约定,因此编译器可以随意执行任何操作.它可以使程序输出"dksjalk是一个ninnyhead !!!" 从技术上讲,它仍然是正确的.
原因是这printf()
是一个非常愚蠢的功能.它根本不检查类型.如果你说第一个参数是一个int
(这就是你所说的%d
),它认为你只需要一个所需的字节int
.在这种情况下,假设您的机器使用四字节int
和八字节double
(float
转换为double
内部printf()
),前四个字节a
将只是零,并打印出来.