考虑以下C
代码(在线可用io.c):
#include <stdio.h>
int main () {
float f;
char c;
scanf ("%f%c", &f, &c);
printf ("%f t %c", f, c);
return 0;
}
当输入为100f
时,它输出100.000000 f
。然而,当输入是100e
时,它只输出100.000000
,而不输出e
。这是怎么回事?100e
不是一个无效的浮点数吗?
这(可以说)是一个glibc错误。
这种行为显然违反了标准。然而,它在其他实现中也有体现。有些人认为这是标准中的一个错误。
根据标准,输入项被定义为不超过任何指定字段宽度的最长输入字符序列,并且是匹配输入序列的前缀。所以100e
是一个输入项,因为它是匹配输入序列的前缀,比如100e1
,但输入中任何更长的字符序列都不是。此外,如果输入项不是匹配的序列,则指令的执行失败:这种情况是匹配失败。100e
不是一个匹配的序列,因此标准要求指令失败。
标准不能告诉scanf
接受100
并从e
继续扫描,正如一些人所期望的那样,因为stdio
只有一个字符的有限推回。因此,在读取了100e
之后,实现必须至少再读取一个字符,比如说一个特定的换行符,然后推回换行符和e
,而这并不总是可以做到的
我认为这显然是一个非常不清楚的灰色区域。
如果你是C库的实现者(或者X3J11委员会的成员),你必须担心这类事情——有时会担心很多。你必须担心边缘案例,有时边缘案例可能特别尖锐。
然而,你并没有在你的问题上加上";语言律师;标签,所以也许你不担心一个严格正确的官方解释。
如果你不是C库的实现者或X3J11委员会的成员,我会说:不要担心";右";答案是!你不必担心,因为你不在乎,因为你会疯狂地编写对这个问题敏感的代码——正是因为这是一个明显的灰色地带。(即使你确实弄清楚了什么是正确的行为,你是否相信世界上每个C库的每个实现者都会一直实现这种行为?)
我想说,在";不用担心";,而不是编写对这个问题敏感的代码。
-
根本不要使用
scanf
(用于任何用途)。这是一个令人讨厌、不精确、不完美的函数,除了——也许——在你第一次学习C时,把数字输入你写的前几个程序之外,它对任何事情都没有好处。之后,scanf
在任何严肃的程序中都没有用。 -
不要安排你的代码和数据,这样它就必须面对模糊的输入,比如";CCD_ 19";首先。不管怎样,它是从哪里来的?它是用户可能键入的输入吗?正在从数据文件中读取数据?它是预期的还是意外的,正确的还是不正确的输入?如果您正在读取数据文件,您是否可以控制写入数据文件的代码?您能保证浮点字段总是被适当地分隔吗?而不是偶尔会附加随机字母字符吗?
-
如果确实必须解析可能包含有效浮点数、可能附加了随机字母字符、因此可能像这样模棱两可的输入,我建议您改用
strtod
,它可能会得到更好的定义和实现。
在"%f %c"
之间留出一个空格,并且当您要输入输入时,确保两个输入之间有一个空格。我假设你只是想打印一个字符。
来自C标准(6.4.4.2浮动常数)
decimal-floating-constant:
fractional-constant exponent-partopt floating-suffixopt
digit-sequence exponent-part floating-suffixopt
和
exponent-part:
e signopt digit-sequence
E signopt digit-sequence
如果您将以以下方式更改printf
的呼叫
printf ("%e t %dn", f, c);
你会得到输出
1.000000e+02 10
即变量CCD_ 23得到了新的行字符CCD_。
scanf的实现方式似乎是这样的,即符号e
被解释为浮点数的一部分,尽管符号后面没有数字。
根据C标准(7.21.6.2 fscanf函数)
9从流中读取输入项,除非规范包括一个n说明符输入项被定义为最长不超过任何指定字符的输入字符序列字段宽度和w是匹配输入的或前缀sequence.278)输入项后的第一个字符(如果有的话)仍然未读。
因此100e
是用于浮点数的匹配输入字符序列。