我遇到了这段代码:
#include<stdio.h>
void main()
{
int x;
float t;
scanf("%f",&t);
printf("%d\n",t);
x=90;
printf("%f\n",x);
{
x=1;
printf("%f\n",x);
{
x=30;
printf("%f\n",x);
}
printf("%f\n",x);
}
printf("%f\n",x);
}
瞥了一眼,我把它想象成标准中引用的一些未定义的输出:
警告:printf使用其第一个参数来决定有多少参数 跟随他们的类型。它会变得困惑,你会得到 错误的答案,如果没有足够的论据,如果他们是 错误的类型。
但是输出并没有让我离开这个问题而没有给予第二个想法 (给出的输入是23)。
23
0
23.000000
23.000000
23.000000
23.000000
23.000000
为什么总是23.00000?编译器实际上在这里尝试做什么?为什么它会打印x
的值,而不是弄乱存储在t
的值?它是否有任何解释,因为似乎有一些关于这个未定义的输出(双关语)的定义。
我在32位机器上使用gcc编译器。
答案 0 :(得分:10)
程序行为未定义,允许编译器执行任何操作。
那就是说,我能够在我的系统上重现这种行为,一瞥装配输出就会发生什么:
printf("%d\n",t);
首先将浮点值从t
加载到CPU寄存器%xmm0
中,该寄存器在我的平台上用于将浮点参数传递给函数。该调用不会被printf()
调用访问,因为它正在寻找整数输入。
printf()
的后续调用都没有将任何值加载到%xmm0
,因为您没有将任何浮点值传递给printf或任何其他函数。但是当每个printf
在其格式字符串中遇到%f
时,它会从%xmm0
读取,其中仍然包含23.0
汇编输出,使用更简单的float t = 23.0;
.LCPI0_0:
.quad 4627167142146473984 # double 2.300000e+01
...
movl $.L.str, %edi # .L.str is "%d\n"
movsd .LCPI0_0(%rip), %xmm0 # 23.0 stored in xmm0 here
movb $1, %al
callq printf # this printf will print %esi
movl $90, %esi # 90 stored in %esi here
movl $.L.str1, %edi # .L.str1 is "%f\n"
xorb %al, %al
callq printf # but this printf will print %xmm0
答案 1 :(得分:1)
如果您真的想知道这里产生什么行为,您需要查看汇编代码(完全损坏的)C代码正在为您的特定编译器和CPU转换。