C代码输出的说明

Ter*_*nal 3 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);
}  
Run Code Online (Sandbox Code Playgroud)

看了一眼,我认为它是标准中引用的一些未定义的输出:

警告:printf使用其第一个参数来决定跟随的参数数量以及它们的类型.它会变得混乱,如果没有足够的参数,如果它们是错误的类型,你会得到错误的答案.

但是输出结果并没有让我离开这个问题而不给它第二个想法.
(给出的输入是23).

23 
0
23.000000
23.000000
23.000000
23.000000
23.000000
Run Code Online (Sandbox Code Playgroud)

为什么总是23.00000?编译器实际上在这里尝试做什么?而不是弄乱存储的值x,为什么它打印的值t?它是否有任何解释,因为似乎有一些关于这个未定义的输出(双关语)的定义.

编辑:

我在32位机器上使用gcc编译器.

Cub*_*bbi 10

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

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

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

由于您没有将任何浮点值传递给printf或任何其他函数,因此后续调用printf()任何值%xmm0都没有加载.但是当每个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
Run Code Online (Sandbox Code Playgroud)