因此,通过Google进行快速搜索以fflush(stdin)
清除输入缓冲区,就会发现许多网站警告不要使用它。但这正是我的CS教授教课的方式。
使用有多糟糕fflush(stdin)
?即使我的教授正在使用它,并且它似乎可以正常工作,我还是应该放弃使用它吗?
因此,通过Google进行快速搜索以fflush(stdin)
清除输入缓冲区,就会发现许多网站警告不要使用它。但这正是我的CS教授教课的方式。
使用有多糟糕fflush(stdin)
?即使我的教授正在使用它,并且它似乎可以正常工作,我还是应该放弃使用它吗?
fflush()
上的输入流,甚至定义它以同样的方式(奇迹中的奇迹)。用于POSIX,C和C ++的标准fflush()
没有定义行为,但是没有一个阻止系统定义行为。如果您要编码以获得最大的可移植性,请避免fflush(stdin)
; 如果您要为定义行为的平台进行编码,请使用它-但要注意它不是可移植的。
fflush(stdin);
不会清除输入。
fflush(stdin)
。
If the stream was opened in read mode, or if the stream has no buffer, the call to fflush has no effect, and any buffer is retained
,而Linux文档说For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.
的方式并不完全相同,Windows保留缓冲区,而Linux丢弃缓冲区。
Answers:
简单:这是未定义的行为,因为fflush
它是在输出流上调用的。这是C标准的摘录:
int fflush(FILE * ostream);
ostream指向未输入最新操作的输出流或更新流,fflush函数使该流的所有未写入数据都将被传递到主机环境中,并被写入文件中;否则,行为是不确定的。
因此,这不是“有多严重”的问题。fflush(stdin)
是完全错误的,你不能使用它,永远。
fflush
在第一段中,任何对的引用都清楚地表明它是针对输出流的,您不必为此记住C标准!
fflush(stdin)
了好多年,直到我发现它是UB(偶然)
fflush()
丢弃已从基础文件中获取但未被应用程序使用的所有缓冲数据。流的状态不受影响。” 尽管它是UB,但某些实现似乎在不提及其相对于标准的地位的情况下做出了保证。
fflush(stdin)
比仅实现定义的行为差很多。即使它确实按照大多数人的意愿工作,也将是可怕的。想象一下,如果stdin不是一个笨拙地输入内容的人,而是来自另一个程序或shell重定向:它会读取文件的开头,然后仅删除其余部分。认为stdin总是像人类操作员一样慢,真是愚蠢。
将注释转换为答案-并扩展它们,因为问题会定期出现。
fflush(stdin)
为未定义行为的POSIX,C和C ++标准fflush()
明确说明该行为是未定义的,但它们都没有防止系统定义它。
ISO / IEC 9899:2011(C11标准)说:
§7.21.5.2冲洗功能
¶2如果
stream
指向未输入最新操作的输出流或更新流,则该fflush
函数会使该流的任何未写入数据被传递到主机环境中,并被写入文件中;否则,行为是不确定的。
POSIX大多遵循C标准,但确实将此文本标记为C扩展。
[CX]对于打开供阅读的流,如果文件尚未在EOF上,并且该文件是能够搜索的文件,则基础打开文件描述的文件偏移应设置为该流的文件位置,并且任何由流推回流中的字符
ungetc()
或ungetwc()
随后未从流中读取的字符将被丢弃(无需进一步更改文件偏移)。
请注意,终端无法搜索;都不是管道或插座。
fflush(stdin)
Microsoft和Visual Studio运行时定义fflush()
输入流上行为的定义。
如果打开了流供输入,请
fflush
清除缓冲区的内容。
Cygwin是一个相当常见的平台示例,在该平台上
fflush(stdin)
不会清除输入。
这就是为什么我的评论的此答案版本注释为“ Microsoft和Visual Studio运行时”-如果您使用非Microsoft C运行时库,则看到的行为取决于该库。
出乎意料的是,Linux名义上也记录了行为fflush(stdin)
,甚至以相同的方式定义了行为(奇迹)。
对于输入流,
fflush()
将丢弃所有已从基础文件中提取但尚未被应用程序使用的缓冲数据。
我对Linux文档说仍然有效感到有些困惑和惊讶fflush(stdin)
。尽管有这样的建议,但它通常在Linux上不起作用。我刚刚查看了Ubuntu 14.04 LTS上的文档;它说了上面引用的内容,但是从经验上讲,它是行不通的-至少当输入流是诸如终端之类的不可搜索的设备时。
demo-fflush.c
#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c; enter some new data\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$
此输出是在Ubuntu 14.04 LTS和Mac OS X 10.11.2上获得的。据我了解,它与Linux手册中所说的相矛盾。如果该fflush(stdin)
操作可行,则必须键入新的一行文本以获取信息以供秒getchar()
读。
根据POSIX标准所说,也许需要更好的演示,并且应该澄清Linux文档。
demo-fflush2.c
#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c\n", c);
ungetc('B', stdin);
ungetc('Z', stdin);
if ((c = getchar()) == EOF)
{
fprintf(stderr, "Huh?!\n");
return 1;
}
printf("Got %c after ungetc()\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
请注意,这/etc/passwd
是一个可搜索的文件。在Ubuntu上,第一行如下所示:
root:x:0:0:root:/root:/bin/bash
在Mac OS X上,前4行如下所示:
##
# User Database
#
# Note that this file is consulted directly only when the system is running
换句话说,Mac OS X/etc/passwd
文件的顶部有注释。非注释行符合常规布局,因此root
输入为:
root:*:0:0:System Administrator:/var/root:/bin/sh
Ubuntu 14.04 LTS:
$ ./demo-fflush2 < /etc/passwd
Got r
Got Z after ungetc()
Got o
$ ./demo-fflush2
Allotrope
Got A
Got Z after ungetc()
Got B
$
Mac OS X 10.11.2:
$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$
Mac OS X行为会忽略(或至少似乎忽略)fflush(stdin)
(因此在此问题上未遵循POSIX)。Linux行为与已记录的POSIX行为相对应,但是POSIX规范说得更谨慎-它指定了能够搜索的文件,但是终端当然不支持搜索。它也没有Microsoft规范有用。
Microsoft记录了以下行为 fflush(stdin)
。显然,它使用本机Windows编译器和C运行时支持库,按Windows平台上的说明工作。
尽管有相反的说明,但是当标准输入是终端时,它在Linux上不起作用,但是它似乎遵循POSIX规范,而该规范的措辞要更为谨慎。根据C标准,行为fflush(stdin)
不确定。POSIX添加了限定符“除非可以查找输入文件”,而终端则不行。行为与Microsoft的行为不同。
因此,可移植代码不使用fflush(stdin)
。与Microsoft平台绑定的代码可以使用它并且可以运行,但是请注意可移植性问题。
如何在Unix系统上从tty输入队列清除未读数据的POSIX标准方法,用于从终端文件描述符中丢弃未读信息(与之类似的文件流stdin
)。但是,该操作低于标准I / O库级别。
我相信您永远不应该致电fflush(stdin)
,原因很简单,您甚至根本不需要尝试刷新输入。实际上,您可能以为只有一个原因,那就是:克服一些scanf
卡住的错误输入。
例如,您可能有一个程序正坐在循环中,使用读取整数scanf("%d", &n)
,并且您发现用户第一次键入非数字字符(如)时'x'
,该程序会进入无限循环。
面对这种情况,我相信您基本上有三个选择:
fflush(stdin)
,则通过调用getchar
读取字符直到\n
通常建议。scanf
读取输入以外的内容。现在,如果您是初学者,scanf
似乎是读取输入内容的最简单方法,因此选择#3既麻烦又困难。但是#2似乎是一个真正的解决方案,因为每个人都知道用户不友好的计算机程序是个问题,因此最好做得更好。因此,太多的新手程序员陷入了困境,觉得他们别无选择,只能做#1。他们或多或少必须使用进行输入scanf
,这意味着它将卡在错误的输入上,这意味着他们必须找出一种方法来清除错误的输入,这意味着他们非常想使用fflush(stdin)
。
我想鼓励所有新手程序员进行一系列不同的权衡:
在您的C编程职业生涯的最初阶段,在您习惯使用其他任何东西之前scanf
,请不要担心输入错误。真。继续并使用上面的#2。这样思考:您是一个初学者,很多事情您尚不知道该怎么做,而您尚不知道如何做的一件事是:应对意外的输入。
请尽快学习如何使用以外的功能进行输入scanf
。到那时,您可以开始优雅地处理错误的输入,并且您将获得更多,更好的技术,根本不需要尝试清除错误的输入。
或者,换句话说,仍然坚持使用的初学者scanf
应该随意使用cop-out#2,当他们准备就绪时,他们应该从那里毕业到#3,而没有人应该使用#1来尝试。完全清空输入(当然不能使用fflush(stdin)
。
fflush(stdin)
,则按通常的建议通过调用getchar
读取字符直到\n
,通常是这样。)。 ”-对getchar()
do的调用在找到字符之前,不要阅读字符s\n
。如果有多个字符,对的一次调用getchar()
将仅获取输入的最后一个字符,而不是所有字符(包括换行符)。此外,getchar()
还可以消耗换行符。
使用fflush(stdin)
同花顺的输入是一种像占卜杖探测水使用形如字母“S”棍子。
帮助人们以某种“更好”的方式冲洗输入,就像冲上S型棍的推土铲,然后说:“不,不,您做错了,您需要使用Y形棍!”。
换句话说,真正的问题不是那fflush(stdin)
不起作用。呼叫fflush(stdin)
是潜在问题的征兆。为什么根本要“刷新”输入? 那是你的问题。
而且,通常情况下,潜在的问题是您正在使用scanf
,它处于许多令人困惑的模式之一,这种模式出乎意料地在输入上留下了换行符或其他空格。因此,最佳的长期答案是学习如何使用比更好的技术进行输入scanf
。