简体   繁体   English

为什么 printf 打印出与它上面的值相同的值?

[英]why printf prints same value as the value above it?

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????

it supposed to print 2.5, i want to know why it doesn't prints this number and what's happens bts?它应该打印 2.5,我想知道为什么它不打印这个数字以及 bts 会发生什么? i tried to find answers on google but i'm not sure how to question this problem我试图在谷歌上找到答案,但我不知道如何质疑这个问题

you have the UB in that code.你在那个代码中有 UB。 in the third printf - you pass integer but printf expects double - UB在第三个 printf 中 - 你传递整数但 printf 期望双倍 - UB

cast it and it will work correctly https://godbolt.org/z/M3ysha投它,它会正常工作https://godbolt.org/z/M3ysha

This is a simplified and not necessarily perfectly accurate answer, but: When you use %f in a printf format statement, it does not mean "take the next argument that was passed, and print it as a float".这是一个简单而不一定是完全准确的答案,但是:当您使用%fprintf格式声明,这并不意味着“采取传递的一个参数,并将其打印为浮动”。 No, instead what it means is "take the next float argument that was passed, and print it."不,它的意思是“获取传递的下一个浮点参数,并打印它”。 But in your third printf call, there was no float argument that was passed (because 5/2 gives the int value 2).但是,在你的第三个printf电话,这是没有通过float参数(因为5/2int值2)。 So when printf went to look in the place where float arguments are passed, by chance it picked up the last actual floating-point number you had passed, even though it was passed by the previous printf call.因此,当printf去查看传递浮点参数的地方时,它偶然地选择了您传递的最后一个实际浮点数,即使它是由前一个printf调用传递的。

This is not guaranteed to happen, and obviously it's not the sort of thing you'd want to depend on, but it explains why you saw what you saw.这不能保证会发生,显然这不是你想要依赖的那种事情,但它解释了为什么你看到了你所看到的。

Good compilers help you avoid this kind of mistake by warning you when the number or type of your printf arguments do not match the format string.好的编译器会在printf参数的数量或类型与格式字符串不匹配时发出警告,从而帮助您避免这种错误。 For example, mine says例如,我的说

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

If your compiler doesn't know how to print warnings like these, you might like to try to find a better one.如果您的编译器不知道如何打印这些警告,您可能想尝试找到一个更好的。

5 and 2 are integers, 5/2 is a integer too and truncated to the next int which is 2 5 和 2 是整数,5/2 也是整数并被截断到下一个整数,即 2

try to cast (double)(5/2)尝试投(双)(5/2)

or use 5.0 / 2.0或使用 5.0 / 2.0

  • C compiler is computing 5 / 2 as an integer: 5 / 2 -> 2. C 编译器将5 / 2计算为整数:5 / 2 -> 2。
  • ptinf() is expecting a %f . ptinf()期待一个%f
  • A buggy cast from int[2] to float[?] on your machine / os / compiler / libc is 30.00000, which printf loyally prints.在您的机器/操作系统/编译器/libc 上从 int[2] 转换为 float[?] 的错误是 30.00000, printf忠实地打印出来。

To demonstrate this we can use a bigger number, 5000 / 3 for instance.为了证明这一点,我们可以使用更大的数字,例如 5000 / 3。 You should properly cast your data, or use the proper literals 5.0 instead of 5.您应该正确地转换数据,或使用正确的文字 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;
}

output:输出:

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 

So, it doesn't matter if it's 5/2 or something else, what matters is the data type.所以,不管是 5/2 还是其他什么,重要的是数据类型。 In your case, the chain of casts: (float) (int) 5/2 = 1 accidentally happens to be equals to 30.0000 float.在您的情况下,转换链: (float) (int) 5/2 = 1意外地等于 30.0000 float。

Also, feeding 2 or 4 bytes integer into printf and telling it to expect 8 bytes (such as passing int and formatting as double), hopefully you'll get a segfault, if you're unlucky enough you'll get memory corruption and hard to track bugs.此外,将 2 或 4 个字节的整数输入 printf 并告诉它期望 8 个字节(例如传递 int 和格式化为 double),希望你会得到段错误,如果你足够不幸,你会得到内存损坏和困难跟踪错误。

The behaviour is due to the way arguments are passed to functions.这种行为是由于参数传递给函数的方式造成的。

For eg, integer values are passed via registers like edx, esi etc. (as long as the arguments fit in available registers).例如,整数值通过 edx、esi 等寄存器传递(只要参数适合可用寄存器)。 However, floating point arguments are passed in xmm* registers (as long as they fit in available registers).但是,浮点参数在 xmm* 寄存器中传递(只要它们适合可用的寄存器)。

So inside the function, the values are fetched from appropriate registers based on the type specifier.因此,在函数内部,根据类型说明符从适当的寄存器中获取值。 So a %d would fetch from edx / esi etc, and %f would fetch from xmm* registers.因此%d将从edx / esi等中获取,而%f将从xmm*寄存器中获取。

In your case, the line printf("%f\\n", (float)a);在您的情况下,该行printf("%f\\n", (float)a); stored the value of a (which is 65) as a real value 65.0 due to typecasting.由于类型转换,将a的值(即 65)存储为实际值65.0 As a result, xxm0 register will have value 65.0.因此, xxm0寄存器的值为 65.0。

Now when the control came to printf("%f\\n", 5/2);现在当控制来到printf("%f\\n", 5/2); , the function argument 5/2 is an integer. ,函数参数5/2是一个整数。 So it doesn't get pushed into xxm0 register.所以它不会被推入xxm0寄存器。 However since %f expects a value in xxm* (in this case xxm0 ) register, it just retries whatever value is there in xxm0.但是,由于%f期望xxm* (在本例中为xxm0 )寄存器中的值,因此它只会重试 xxm0 中的任何值。 In your case, xxm0 had stored the value 65.0 from earlier printf() call.在您的情况下, xxm0已存储来自早期printf()调用的值 65.0 。 So 65.0 is printed.所以 65.0 被打印出来。

The below program should make it more clear:下面的程序应该更清楚:

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
}

During first printf() call, xxm0 = 11.1 and xxm1 = 12.2 .在第一次printf()调用期间, xxm0 = 11.1xxm1 = 12.2 Since all of the arguments in the second printf() evaluate to be integers, so nothing gets stored in xxm* registers (which also means old values of xxm* registers remain unaltered).由于第二个printf()中的所有参数都计算为整数,因此xxm*寄存器中不会存储任何内容(这也意味着xxm*寄存器的旧值保持不变)。 Since %f expects real numbers to be in xxm* registers, it just prints whatever values they hold (in this case values from first printf() statement).由于%f期望实数在xxm*寄存器中,因此它只打印它们保存的任何值(在这种情况下,来自第一个printf()语句的值)。

You can use https://www.godbolt.org to see the generated assembly code.您可以使用https://www.godbolt.org查看生成的汇编代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM