使用fflush(stdin)

因此,快速Googlesearchfflush(stdin)清除input缓冲区会显示许多网站警告不要使用它。 然而这正是我的CS教授如何教课程去做的。

使用fflush(stdin)有多糟? 我应该真的放弃使用它,即使我的教授正在使用它,它似乎工作完美无瑕?

简单:这是未定义的行为,因为fflush是为了在输出stream上调用。 这是C标准的摘录:

int fflush(FILE * ostream);

ostream指向输出stream或没有input最近操作的更新stream,fflush函数会将该stream的所有未写入数据传送到主机环境以写入文件; 否则,行为是不确定的。

所以这不是“多么糟糕”的问题。 fflush(stdin) 显然错误的 ,你永远不要使用它

根据标准, fflush只能用于输出缓冲区,显然stdin不是一个。 但是, 有些编译器提供了fflush(stdin)作为扩展。 在这种情况下,你可以使用它,但是它会影响可移植性,所以你将不再能够使用任何符合标准的编译器,并期望得到相同的结果。

将意见转换为答案 – 并且在问题定期重新出现之后进行扩展。

标准C和POSIX离开fflush(stdin)为未定义的行为

fflush()的POSIX ,C和C ++标准明确声明行为是未定义的,但是没有一个阻止系统定义它。

ISO / IEC 9899:2011 – C11标准 – 说:

§7.21.5.2fflush函数

¶2如果stream指向输出stream或没有input最近操作的更新stream,则fflush函数会将该stream的所有未写入的数据传送到主机环境以写入文件; 否则,行为是不确定的。

POSIX主要遵循C标准,但它确实将此文本标记为C扩展。

[CX]对于打开读取的stream,如果文件不在EOF中,并且该文件是能够查找的文件,则将底层打开文件描述的文件偏移设置为stream的文件位置,并且任何ungetc()ungetwc()后面没有从stream中读取的字符将被丢弃(不会进一步改变文件偏移量)。

请注意,terminal不能寻求; pipe道和sockets也不是。

微软定义了fflush(stdin)的行为,

Microsoft和Visual Studio运行库定义了在inputstream上定义fflush()的行为。

如果该stream打开input, fflush清除缓冲区的内容。

MM 注意到 :

Cygwin是fflush(stdin)不清除input的一个相当普通的平台的例子。

这就是为什么我的评论注释“Microsoft和Visual Studio运行时”的答案版本 – 如果您使用的是非Microsoft C运行时库,则您看到的行为取决于该库。

Linux的文档和实践似乎相互矛盾

令人惊讶的是, Linux名义上也logging了fflush(stdin)的行为,甚至以同样的方式(奇迹的奇迹)来定义它。

对于inputstream, fflush()会丢弃已经从底层文件中获取但尚未被应用程序使用的缓冲数据。

我对Linux文档说fflush(stdin)会起作用,我仍然有些困惑和惊讶。 尽pipe有这个build议,但它通常不适用于Linux。 我刚刚检查了Ubuntu 14.04 LTS上的文档; 它说上面引用了什么,但从经验上来说,它不起作用 – 至less当inputstream是一个不可search的设备,如terminal。

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)操作起作用,我将不得不input新的一行文本来获取第二个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是一个可search的文件。 在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行为忽略(或者至less似乎忽略) fflush(stdin) (因此在这个问题上不遵循POSIX)。 Linux的行为对应于已经logging的POSIX行为,但是POSIX规范在说明上要小心得多 – 它指定了一个能够search的文件,但terminal当然不支持查找。 它也比微软规范less得多。

概要

Microsoftloggingfflush(stdin)的行为。 显然,它使用本地Windows编译器和C运行时支持库在Windows平台上logging。

尽pipe有相反的文档,但在标准input是terminal的情况下,它在Linux上不起作用,但似乎遵循POSIX规范,这是更加谨慎的措辞。 根据C标准, fflush(stdin)的行为是未定义的。 POSIX添加限定符'除非input文件是可search的',terminal不是。 行为与微软的不一样。

因此,可移植代码不使用fflush(stdin) 。 与微软平台绑定的代码可能会使用它,它会工作,但要小心可移植性问题。

POSIX方式丢弃来自文件描述符的未读terminalinput

如何从Unix系统上的ttyinput队列中刷新未读数据,说明POSIX标准的从terminal文件描述符(而不是stdin等文件stream)丢弃未读信息的方法。 但是,这是在标准I / O库级别下运行的。