C代码输出的说明

时间:2011-07-13 16:28:15

标签: c undefined-behavior

我遇到了这段代码:

#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编译器。

2 个答案:

答案 0 :(得分:10)

程序行为未定义,允许编译器执行任何操作。

那就是说,我能够在我的系统上重现这种行为,一瞥装配输出就会发生什么:

printf("%d\n",t);首先将浮点值从t加载到CPU寄存器%xmm0中,该寄存器在我的平台上用于将浮点参数传递给函数。该调用不会被printf()调用访问,因为它正在寻找整数输入。

printf()的后续调用都没有将任何值加载到%xmm0,因为您没有将任何浮点值传递给printf或任何其他函数。但是当每个printf在其格式字符串中遇到%f时,它会从%xmm0读取,其中仍然包含23.0

来自CLang的

汇编输出,使用更简单的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转换。