为什么scanf()需要使用"%lf"来表示double类型,而printf()只需要用"%f"即可呢?

195
为什么在读取double时,scanf()需要在"%lf"中使用L,而printf()不管其参数是double还是float都可以使用"%f"?
示例代码:
double d;
scanf("%lf", &d);
printf("%f", d);

1
我不明白你在这里所说的指针是什么意思。 在scanf中,我们只传递变量的地址(&variable),那么指针在哪里呢? - user1461834
12
在 C 语言中,当你使用一元运算符 & 来“取地址”一个变量时,这个操作的结果是一个指向该变量在内存中存储位置的指针。正是这个指针被传递给了 scanf 函数。 - zwol
这是另一篇关于以下内容的文章:https://dev59.com/vmDVa4cB1Zd3GeqPdXPf - vimalpt
5个回答

221

因为C语言会将浮点数在可变参数函数中提升为双精度类型,而指针不会被提升到任何类型,因此您应该使用%lf%lg%le(在C99中使用%la)来读取双精度浮点数。


35

自从 C99 以来,在C语言中,格式化标识符与浮点参数类型的匹配在 printf 和 scanf 函数之间是一致的。

  • %f 表示 float 类型
  • %lf 表示 double 类型
  • %Lf 表示 long double 类型

恰好当传递的可变参数的类型是 float 时,这些参数会被隐式转换为 double 类型。这就是为什么在 printf 中,格式化标识符 %f%lf 是等价和可互换的原因。在 printf 中可以将 %lf 用于 float 或者用 %f 来表示 double

但实际上没有必要这样做。不要使用 %f 来打印类型为 double 的变量。尽管在 C89/90 的时代很普遍,但这是个坏习惯。在 printf 中使用 %lf 来打印类型为 double 的变量,并将 %f 保留给 float 类型的参数。


5
我认为在printf中使用'%f'是一个好习惯,因为这样你的代码始终能正常工作,而使用'%lf'可能会失败,如果编译器没有C99兼容库的话。不幸的是,这种情况在现实中确实发生过。 - M.M
1
自从C99以来,C语言中的格式说明符和浮点参数类型在printfscanf之间的匹配是一致的。请注意,这并不意味着使用相同的格式说明符意味着由[f]printf()写入的数据可以被[f]scanf()读取。通常情况下,对于printf()使用的相同格式说明符,在scanf()中使用将不能成功读取数据。例如,printf()"%d"格式说明符可以插入空格填充,但是在scanf()调用中,相同的"%d"格式说明符将跳过它们。 - Andrew Henle

17

scanf需要知道&d所指向的数据大小才能正确地填充,而变长函数会将浮点数提升为双精度浮点数(不完全确定原因),所以printf总是得到一个double


1
可变参数函数非常脆弱,因为它需要知道传递给它的所有参数的确切类型和大小,并且无法在编译时强制执行。如果变量类型错误,则会读取错误的值;如果大小不正确,则之后的所有变量也将被错误读取。如果可以传递两种不同大小的浮点数,则会引起各种令人讨厌且容易忽略的问题。 - mwfearnley

7

否则,scanf会认为您传递的是指向比double更小的float类型的指针,并返回一个不正确的值。


3

在C表达式中使用float或double值,得到的值都是double类型,因此printf无法区分。而指向double的指针必须明确地告知scanf与指向float的指针不同,因为指针指向的内容才是重要的。


5
在这种情况下,float被转换为double*,因为这些参数是可变长度参数列表的一部分,在C语言中,并不总是将float转换为double。 - Robert Gamble
1
在 C 语言的早期版本中,float 值在表达式中会自动提升为 double。但这个规则在标准 C 中被废弃了。通常情况下,在表达式中 float 不会被提升为 double。只有当作为可变参数传递时,它才会被提升为 double,这也是这种情况发生的原因。 - AnT stands with Russia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接