C代码输出说明

C代码输出说明,c,undefined-behavior,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

我遇到了以下代码:

#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);
}  
为什么总是23.00000?编译器在这里实际要做什么?与其乱搞存储在
x
的值,为什么它要打印
t
的值?它是否有任何解释,因为这个未定义的输出似乎有一些定义(双关语)

编辑:
我在32位机器上使用gcc编译器。

如果你真的想知道是什么产生了这里的行为,你需要看看这个(完全破碎的)C代码被翻译成的汇编代码,用于你的特定编译器和CPU。

程序行为未定义,编译器可以做任何事情

也就是说,我能够在我的系统上重现这种行为,只要看一眼程序集输出就会知道发生了什么:

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

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

使用带有
float t t=23.0的更简单程序,从CLang获得汇编输出

.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

推测——对于编译器,int和float具有不同大小的表示形式。当您将浮点视为int时,它会看到浮点中未使用的位并显示0。当你将一个int显示为float时,它会在堆栈中查找int部分,以找到完全不同值的位。float和int在我的机器上的大小都是相同的4字节。Cubbi有正确的答案;不是堆栈上的位置,而是使用哪个寄存器(当然,高度依赖于编译器/处理器的行为)。这澄清了一切。这本身就是一种非故意的黑客行为。
.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