当使用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
,如果这很重要。
2条答案
按热度按时间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
和/或
相对于结果是等价的。
请注意,一些程序员在转换规范中使用长度修饰符
l
来输出double,例如%lf
。但是,长度修饰符不起作用,应将其删除。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
的行为与例如自定义可变参数函数。