为什么printf会打印出与其上方相同的值?

时间:2019-11-23 14:07:22

标签: c printf

int main(void)
{
    int a = 65;
    char c = (char)a;

    printf("%c\n", c);              // output: A
    printf("%f\n", (float)a);       // output: 65.000000
    printf("%f\n", 5/2);            // output: 65.000000 why???


    return 0;
}
//why printf("%f\n", 5/2); prints same number as a number above????

它应该打印2.5,我想知道为什么它不打印这个数字,会发生什么? 我试图在Google上找到答案,但是我不确定如何质疑这个问题

5 个答案:

答案 0 :(得分:3)

该代码中包含UB。在第三个printf中-您传递整数,但printf需要双精度-UB

广播它,它将正常工作 https://godbolt.org/z/M3ysha

答案 1 :(得分:2)

这是一个简化且不一定完全正确的答案,但是:当您在%f格式的语句中使用printf时,不是的意思是“采用下一个参数”已通过,然后将其打印为浮动”。不,相反,它的意思是“获取传递的下一个float参数,然后打印它”。但是在您的第三个printf调用中,没有传递 (因为5/2给出了int的值为2)。因此,当printf转到传递浮点参数的位置时,即使它是由先前的printf调用传递的,它还是偶然会获得您传递的最后一个实际浮点数。

并不能保证会发生这种情况,显然这不是您想要依靠的事情,但这可以解释您看到自己看到的东西的原因。

好的编译器会在printf参数的数量或类型与格式字符串不匹配时警告您,从而避免了这种错误。例如,我说

warning: format specifies type 'double' but the argument has type 'int'

如果您的编译器不知道如何打印此类警告,则您可能希望尝试找到更好的警告。

答案 2 :(得分:0)

5和2是整数,5/2也是整数,并被截断为下一个int 2

尝试投射(double)(5/2)

或使用5.0 / 2.0

答案 3 :(得分:0)

  • C编译器将5 / 2计算为整数:5/2->2。
  • ptinf()期望有%f
  • 在计算机/操作系统/编译器/ libc上从int [2]转换为float [?]的越野车是30.00000,printf会打印出来。

为了证明这一点,我们可以使用更大的数字,例如5000/3。您应该正确地转换数据,或使用正确的文字5.0代替5。

#include <stdio.h>

int main(void)
{
  printf("5/3 d:\t\t\t %d\n", 5/3);
  // printf("5/3 n:\t\t\t %n\n", 5/3); // segfault
  printf("5/3 f:\t\t\t %f\n", 5/3);

  printf("(double) 5/3 d:\t\t %d \n", (double) 5/3);
  // printf("(double) 5/3 n:\t\t %n \n", (double) 5/3); // memory corruption
  printf("(double) 5/3 f:\t\t %f \n", (double) 5/3);

  printf("(float) 5/3 d:\t\t %d \n", (float) 5/3);
  // printf("(float) 5/3 n:\t\t %n \n", (float) 5/3); // memory corruption
  printf("(float) 5/3 f:\t\t %f \n", (float) 5/3);

  printf("5/3 d:\t\t\t %d \n", 5000/3);
  // printf("5/3 n:\t\t\t %n \n", 5000/3); // segfault
  printf("5/3 f:\t\t\t %f \n", 5000/3);

  printf("5./3. d:\t\t %d \n", 5./3.);
  // printf("5./3. n:\t\t %n \n", 5./3.); // memory corruption
  printf("5./3. f:\t\t %f \n", 5./3.);

  return 0;
}

输出:

5/3 d:           1
5/3 f:           0.000000
(double) 5/3 d:      33989216 
(double) 5/3 f:      1.666667 
(float) 5/3 d:       33989216 
(float) 5/3 f:       1.666667 
5/3 d:           1666 
5/3 f:           1.666667 
5./3. d:         33989216 
5./3. f:         1.666667 

因此,无论是5/2还是其他值,重要的是数据类型。就您而言,强制转换链:(float) (int) 5/2 = 1恰好等于30.0000 float。

另外,将2或4个字节的整数输入到printf中,并告诉它期望8个字节(例如,传递int和格式化为double的形式),希望您会遇到段错误,如果您不够幸运的话,将获得内存腐败和难以跟踪的错误。

答案 4 :(得分:0)

该行为是由于参数传递给函数的方式引起的。

例如,整数值通过诸如edx,esi等寄存器传递(只要参数适合可用寄存器)。但是,浮点参数在xmm *寄存器中传递(只要它们适合可用的寄存器)。

因此,在函数内部,根据类型说明符从相应的寄存器中获取值。因此,%d将从edx / esi等中获取,而%f将从xmm*寄存器中获取。

在您的情况下,由于类型转换,第printf("%f\n", (float)a);行将a的值(为65)存储为实际值65.0。结果,xxm0寄存器的值将为65.0。

现在,当控件进入printf("%f\n", 5/2);时,函数参数5/2是一个整数。因此它不会被推送到xxm0寄存器中。但是,由于%f期望xxm*(在这种情况下为xxm0)寄存器中有一个值,因此它仅重试xxm0中存在的任何值。在您的情况下,xxm0已存储了先前printf()调用中的值65.0。这样就打印了65.0。

下面的程序应该使它更清楚:

int main(void)
{
    printf("%f\n", 10, 11.1, 12.2, 40);       // 11.100000
    printf("%f %f\n", 5/2, 1, 2, 3, 4);       // 11.100000 12.200000
}

在第一次printf()xxm0 = 11.1xxm1 = 12.2通话期间。由于第二个printf()中的所有参数均为整数,因此xxm*寄存器中没有存储任何内容(这也意味着xxm*寄存器的旧值保持不变)。由于%f期望实数位于xxm*寄存器中,因此它只打印它们持有的任何值(在这种情况下,是第一个printf()语句中的值)。

您可以使用Headless Service查看生成的汇编代码。