【问题标题】:What is printf's behaviour when printing an int as float?将 int 打印为 float 时 printf 的行为是什么?
【发布时间】:2012-01-02 11:25:53
【问题描述】:

我在 windows7 上使用 dev cpp 来编译我的代码。

int d = 0x12;
char* e = (char*)&d;
printf("%d %d\n", sizeof (int), sizeof (float));
printf("%p %p\n", &d, (float*)&d);
printf("%p %p %p %p %p\n", &d, &e[0], &e[1], &e[2], &e[3]);
printf(" %d | %x | %#1x | %#1x | %#1x |%p\n", d,  e[0], e[1], e[2], e[3], &e[0]);
getchar();

4 4 
0028FF40 0028FF40
0028FF40 0028FF40 0028FF41 0028FF42 0028FF43  
18 | 12 | 0 | 0 | 0 |0028FF40

您会看到,如果我使用 %d 打印 d,它可以打印 e 的 4 个字节。但是如果我像下面这样使用 %f ,它会在必须打印 e 的第一个字节的地方显示零。任何人都可以帮助解决为什么会发生这种情况?为什么e的内容要依赖d的格式?

int d = 0x12;
char* e = (char*)&d;
printf("%d %d\n", sizeof (int), sizeof (float));
printf("%p %p\n", &d, (float*)&d);
printf("%p %p %p %p %p\n", &d, &e[0], &e[1], &e[2], &e[3]);
printf(" %f | %x | %#1x | %#1x | %#1x |%p\n", d,  e[0], e[1], e[2], e[3], &e[0]);
getchar();

输出是:

4 4
0028FF40 0028FF40
0028FF40 0028FF40 0028FF41 0028FF42 0028FF43
 0.000000 | 0 | 0 | 0 | 0x28ff40 |76869F1D

【问题讨论】:

标签: c casting floating-point int printf


【解决方案1】:

未定义的行为。

实际上,您所看到的可能是由于%f 使printf 从其参数列表中拉出double,这是8 个字节*。但是d 的大小只有 4 个字节,所以现在所有内容都未对齐。

请记住,printf 是一个可变参数函数,这意味着它完全不是类型安全的; printf 必须在没有编译器帮助的情况下从堆栈中提取原始字节。确保参数与格式字符串精确对应完全取决于您。


* 可能。

【讨论】:

  • 我认为你的回答意味着 e 的字节仍然存在,但只是它从错误的地方读取它们。我可以通过在下一行制作一个强制转换的 printf 来验证。 printf(" %f | %x | %#1x | %#1x | %#1x |%p\n", (float)d, e[0], e[1], e[2], e[3 ], &e[0]);这实际上给出了正确的结果。谢谢你的解释。
【解决方案2】:

你被自动类型提升绊倒了。

您似乎假设由于 sizeof(int) == sizeof(float),您可以将 d 作为 float 或 int 互换传递。但问题是,在 C 中你不能传递浮点数。

在 C 语言中,当您在表达式中使用 char 或 short 时,它会自动转换为 int。同样,当您在表达式中使用浮点数时,它会自动转换为双精度数。

因此,当您执行 printf("%f", d) 时,编译器会在进行函数调用之前将一个 int(即四个字节)压入堆栈。然后在函数中,printf() 看到“%f”,并从堆栈中拉出 8 个字节。其中四个字节是您的“d”,而其他四个字节是发生在那里的任何内容。在您的示例中,e[0] 和 e[1]。

如果要将“d”打印为浮点数,则需要显式转换它,或分配给浮点变量。在任何一种情况下,编译器都会在调用 printf() 之前将其转换为 double。 (注意:听起来很奇怪,“%f”用于打印双精度数,而不是浮点数。您不能打印浮点数。您可以使用“%f”和“%lf”读取浮点数或双精度数,在scanf(),但在 printf() 中,"%f" 打印双精度。)

【讨论】:

  • “当你在表达式中使用浮点数时,它会自动转换为双精度数。” 这是错误的。浮点数不会在所有表达式上隐式转换为双精度,除非表达式中的其他操作数是双精度(通常的算术转换)。您所追求的称为默认参数提升,这在A)float被传递给没有原型的函数,或B)float被传递给带有可变参数的函数时发生(原型以, ... 结尾)。
【解决方案3】:

是的,您从堆栈中拉出 8 个字节 - 其中四个是 0x00000012,其余的取决于编译器(可能是构造 printf 的堆栈帧时压入堆栈的返回地址)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 1970-01-01
    相关资源
    最近更新 更多