C代码输出的解释

3
我看到了这段代码:

我看到了这段代码:

#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个回答

10
该程序的行为是未定义的,编译器可以做任何事情。
话虽如此,在我的系统上我能够重现这种行为,并且查看汇编输出可以看到发生了什么: printf("%d\n",t); 首先将浮点值从 t 加载到 CPU 寄存器 %xmm0 中,该寄存器在我的平台上用于将浮点参数传递给函数。这次调用 printf() 没有访问该寄存器,因为它正在寻找整数输入。
随后的没有一个 printf() 调用将任何值加载到 %xmm0 中,因为您没有将任何浮点值传递给 printf() 或其他任何函数。但是当每个 printf() 在其格式字符串中遇到 %f 时,它会从 %xmm0 中读取,该寄存器仍然包含 23.0
使用简单程序和 float 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

谢谢,这解释了一切。这本身就是一种意外的黑客行为。 - Terminal

1

如果你真的想知道这里产生了什么行为,你需要查看针对你特定编译器和CPU所翻译的这段(完全错误的)C代码的汇编代码。


1
推测——对于您的编译器,整数和浮点数具有不同大小的表示。当您将浮点数视为整数时,它会查看浮点数中未使用的位并显示0。当您将整数显示为浮点数时,它会在堆栈上查找整数部分之后的位,以找到完全不同值的位。 - antlersoft
在我的机器上,浮点数和整数的大小相同,都是4个字节。 - Terminal
Cubbi有正确的答案;不是堆栈上的位置,而是使用哪个寄存器(当然高度依赖于编译器/处理器的行为)。 - antlersoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接