C语言 为什么在printf中int到float的转换失败?

3ks5zfa0  于 2023-06-21  发布在  其他
关注(0)|答案(2)|浏览(141)

当使用int到float的隐式转换时,它会因printf()而失败

#include <stdio.h>

int main(int argc, char **argv) {
    float s = 10.0;
    printf("%f %f %f %f\n", s, 0, s, 0); 
    return 0;
}

当使用gcc -g scale.c -o scale编译时,它会输出垃圾

./scale
10.000000 10.000000 10.000000 -5486124068793688683255936251187209270074392635932332070112001988456197381759672947165175699536362793613284725337872111744958183862744647903224103718245670299614498700710006264535590197791934024641512541262359795191593953928908168990292758500391456212260452596575509589842140073806143686060649302051520512.000000

如果我显式地将整数转换为浮点数,或者使用0.0(即double),它将按设计工作。

#include <stdio.h>

int main(int argc, char **argv) {
    float s = 10.0;
    printf("%f %f %f %f\n", s, 0.0, s, 0.0); 
    return 0;
}

当用gcc -g scale.c -o scale编译时,它会输出预期的输出

./scale
10.000000 0.000000 10.000000 0.000000

发生了什么?

我使用gcc (Debian 10.2.1-6) 10.2.1 20210110,如果这很重要。

gopyfrb3

gopyfrb31#

转换说明符f需要一个double类型的对象。通常sizeof( double )等于8,而sizeof( int )等于4。而且整数和双精度数有不同的内部表示。
使用不正确的转换说明符会导致未定义的行为。
来自C标准(7.21.6.1 fprintf函数)
9如果转换规范无效,则行为未定义。275)如果任何参数不是对应转换规范的正确类型,则行为未定义。
对于float类型的对象,由于 * 默认参数提升 ,它们被转换为double类型。
来自C标准(6.5.2.2函数调用)
6如果表示被调用函数的表达式的类型不包括原型,则对每个参数执行整数提升,并且具有float类型的参数被提升为double。这些称为
默认参数提升*。
所以这些调用printf

printf("%f %f %f %f\n", s, 0.0, s, 0.0);

和/或

printf("%f %f %f %f\n", s, 0.0f, s, 0.0f);

相对于结果是等价的。
请注意,一些程序员在转换规范中使用长度修饰符l来输出double,例如%lf。但是,长度修饰符不起作用,应将其删除。

wlwcrazw

wlwcrazw2#

一个可变参数函数需要一些关于它传递的参数的数量和类型的信息,因为它必须使用va_arg主动检索这些参数(这需要参数的类型作为参数检索)。在printf的情况下,此信息位于第一个参数(格式字符串)中。
如果格式字符串中有一个%f,可变参数函数将期望一个double参数1,并将使用va_arg检索它。
但是编译器并不知道这一点。它不考虑格式字符串--它看到的只是参数的 actual 类型,在本例中是int
所以有一个int放进去,一个double 1拿出来。这恰好是未定义的行为。
你的 * 观察到的 * 行为是因为你的第一个平台传递浮点值与整数值不同-整数0被放在一个地方,但va_arg从另一个地方读取-实际上,第一次阅读第二个实际的float 1参数,并且如果你传递了一个,无论在哪里,第三个 * float 1值都会被放进去。
1 float变量函数的参数会自动扩展为double
2它 * 可以 *,如果格式字符串和参数类型不匹配,现代编译器可以生成 * 警告 *。但是编译器不允许根据格式字符串改变生成的代码,因为这会使printf的行为与例如自定义可变参数函数。

相关问题