我在玩C;看看这个:
#include <stdio.h>
#include <stdlib.h>
void main() {
printf("%d\n", 1.5);
printf("%f", 0);
}
我期待输出:
0
0.000000
但它会打印:
0
1.500000
第一个printf()
是否已将1.5
传递给第二个printf()
?
PS:我知道(%d
代表,%f
代表浮点数。正如我所提到的,我只是在搞乱代码。
PS2:我使用的是DevC ++&amp;代码::块。
答案 0 :(得分:13)
根据C标准,行为未定义,以下是您系统上可能发生的情况:
printf("%d\n", 1.5);
main
将浮点值1.5
作为第一个XMM注册表中的double传递并调用printf()
。printf()
不修改XMM寄存器,因为如果不执行任何浮点操作来处理格式"%d"
。它从不同的位置检索要打印的值:寄存器或堆栈,该值恰好是0
。printf("%f", 0);
,main
不会更改XMM,因为它通过其他位置,寄存器或堆栈传递int
值0
printf()
最终从XMM寄存器中获取格式double
的{{1}}值,其中%f
之前已存储过1.5
。因此输出1.500000
。以上都不能以任何方式得到保证,但这可能是您感兴趣的解释。不同的系统可能以不同的方式处理参数传递,它们是ABI(应用程序二进制接口)的一部分。
只是为了好玩,您可能想尝试这种变化:
printf("first %d, second %f\n", 1.5, 42);
在我的系统上输出first 42, second 1.500000
答案 1 :(得分:7)
你有一些undefined behavior(所以任意不好的事情可以happen,你不应该期待任何好事。使用%f
时,printf函数需要double
(请注意,当作为参数传递时,float
会被提升为double
,但0
是类型为int
的文字。此外,不同的编译器(甚至是同一编译器的不同版本)或不同的优化标志会产生不同的不良影响。
请在What Every C programmer should know about undefined behavior上阅读Lattner的博客。
(对未定义行为的良好态度是尽力避免它;不要浪费时间去理解具体发生的事情;但要把UB视为非常脏或者你总是避免的“生病”
要解释观察到的行为,您需要深入了解特定实现的细节,特别是ABI和calling conventions(对于可变函数àlaprintf
)。另外,查看生成的汇编程序代码(使用GCC,使用gcc -fverbose-asm -S -O1
进行编译);有可能在一个寄存器(或某个call stack槽)中传递了一个不同于int
参数的双参数(因此printf
函数正在使垃圾发生在那里地点或登记);另请注意,sizeof(int)
通常可能为4,但sizeof(double)
可能为8(因此数据量甚至不正确)。
为避免此类错误,请习惯使用良好的编译器(例如GCC域中的Clang/LLVM或free software)进行编译,并启用所有警告和调试信息(例如编译)将gcc -Wall -Wextra -g
与GCC一起使用。编译器会警告你。
void main()
是非法的。它应该至少为int main(void)
,最好是int main(int argc, char**argv)
,你应该注意这些论点。
通过您的示例,gcc -Wall -Wextra
(使用GCC 7)告诉(对于您的源文件april.c
):
april.c:4:10: warning: return type of ‘main’ is not ‘int’ [-Wmain]
void main() {
^~~~
april.c: In function ‘main’:
april.c:5:14: warning: format ‘%d’ expects argument of type ‘int’,
but argument 2 has type ‘double’ [-Wformat=]
printf("%d\n", 1.5);
~^
%f
april.c:6:14: warning: format ‘%f’ expects argument of type ‘double’,
but argument 2 has type ‘int’ [-Wformat=]
printf("%f", 0);
~^
%d
注意:Dev-C++和CodeBlocks 不是编译器,但是IDE s。他们都在您的系统上运行了一些外部 compiler(可能GCC为MinGW。