相同的表达不同的答案

时间:2019-07-28 18:20:01

标签: c

在注释了第一个// printf()的情况下,我得到的答案是0.000000,而在不注释第一个printf()的情况下,我得到了5.000000和5.000000?

#include  <stdio.h>

int main(void)
{    
    float d;
    d=2+2*3/2;
    printf("%f\n",d);//5.000000
    printf("%f",2+2*3/2);//5.000000
    return 0;
}
#include  <stdio.h>

int main(void)
{    
    float d;
    d=2+2*3/2;
    //printf("%f\n",d);
    printf("%f",2+2*3/2);//0.000000
    return 0;
}

2 个答案:

答案 0 :(得分:1)

如另一个答案所述,当您尝试使用int打印%f值时,该行为是不确定的,因此任何事情都可能发生。

在对另一个答案的评论中,您建议“如果我对printf撒谎,那么在两个示例中都应该是相同的”,我回答了另一条有关车祸不可预测性的评论。

但是现在我将尝试回答您的问题,但是有一个要点:您将必须首先参加另一场小演讲。

当您听到某种形式上不确定的东西(这适用于任何系统,而不仅是C编程)时,实际上只有一种理智的做法,即:不要那样做。避免像瘟疫一样。这是毒药,是邪恶的。它看起来似乎可以做一会儿合理的事情,但时间长到足以使您着迷或使您依赖它,然后它会转变,使您被遗弃。

问这个事物为什么未定义,或者预期的行为范围是什么,或者为什么您一次看到一种行为而又一次看到另一种行为,也不是一个好主意。通常,这些问题都不是可以回答的。同样,即使提供了答案,一些程序员(不是我,我知道,但是有些人)也会尝试使用答案来证明保留一段包含他们认为可以预测其行为的未定义行为的代码是合理的。这个练习很危险。

随着编译器变得越来越复杂,优化变得更加激进,当您的代码包含未定义的行为时发生的事情变得越来越陌生。您真的无法预测它,您确实必须避免它。

但是:您的程序呢?为什么打断电话

printf("%f\n", 2+2*3/2);

孤立呼叫时打印0,但是在另一个呼叫之前打印5时(看似正确的答案)?

要回答这个问题,我们需要知道值是如何传递给函数的,并且这在机器之间是不同的,这是使此类问题难以回答的因素之一。

在许多机器上,类型int的前几个值传递给两个或三个为整数保留的寄存器中的函数,类型double的前几个值传递给函数两个或三个保留用于双精度的寄存器。 (我知道您的程序使用了float类型的变量,但是在调用printf时,浮点数总是被提升为两倍。)

因此,这很容易看出为什么在这样的机器上像这样的呼叫

printf("%f\n", 2+2*3/2);

不起作用。表达式2+2*3/2的类型为int,因此其值将被放入保留为整数的寄存器的第一个中。但是,当printf在格式字符串中找到%f时,它会说“ %f是双精度的,因此我的调用方应该已经传递了一个双精度,所以我将从中获取一个值。第一个保留给双打的寄存器。”但是没有任何内容保留在为双精度保留的第一个寄存器中,因此打印出的值就是已经发生的任何事情,可能只是0。

但是(您可能会看到前进的方向),如果有一个先前的调用正确打印了浮点5,则得到5.0放在保留用于双打的第一个寄存器中,所以当您错误地调用

printf("%f\n", 2+2*3/2);

这次,printf转到该寄存器并找到5,然后打印出来。

为了检验这个假设,我只是尝试了一个实验。我已经确定我的机器在两个程序上的表现与您的机器相同,第一个程序打印5和5,第二个程序打印0。 (实际上,这有点令人惊讶-由于未定义的行为意味着任何事情都可能发生,因此它甚至更有可能在不同操作系统和不同编译器的不同机器上执行不同的操作。)因此,我尝试了以下代码:

printf("%f\n", 7.7);
printf("%f\n", 2+2*3/2);

看看您是否能猜出它打印的内容。 (而且,由于我们的机器似乎很相似,因此您也可以尝试一下,并在计算机上获得相同的结果。)

答案 1 :(得分:0)

TL; DR

您的代码(在两种情况下均为 )通过向期望int的函数发送double来调用未定义的行为。


您的代码调用未定义的行为。这个:

2+2*3/2

int表达式。尽管可以像在第一个示例中将其分配给float一样,将其分配给d并允许进行自动转换(买方要当心;知道您在做什么),但这是< em> 不能将int发送到要求不同的可变参数(或其他函数)。

此:

printf("%f\n",d);

根据C语言的可变函数规则将d隐式提升为double值。在printf中,%f格式说明符用于确定可变参数列表中应该有一个double,并且肯定有一个。注意:升级到double%f毫无关联。后者期望double,但是printf的变异性才是促使floatdouble晋升的原因。

另一方面,

printf("%f",2+2*3/2);

正在向int发送printf,而静止仍期望double,但不再接收。基本上,您对printf撒谎,结果导致混乱的怪异旋转。