转载自这里
清空键盘缓冲区很多种方法,如用fflush(stdin);
rewind(stdin);
等,但是在linux这些都不起作用,还得我今天试了半天都没成功,上网搜了一下发现setbuf(stdin, NULL);
就能直接清空键盘缓冲区了。
以下几个实例:
Sample one
程序的本意很简单,就是从键盘读入两个字符,然后打印出这两个字符的ASCII码值。可是执行程序后会发现出了问题:当从键盘输入一个字符后,就打印出了结果,根本就没有输入第二个字符程序就结束了。例如用户输入字符’a', 打印结果是97,10。这是为什么呢?
【分析】:
scanf()和getchar()函数是从输入流缓冲区中读取值的,而并非从键盘(也就是终端)缓冲区读取。而读取时遇到回车(n)而结束的,这个n会一起读入输入流缓冲区的,所以第一次接受输入时取走字符后会留下字符n,这样第二次的读入函数直接从缓冲区中把n取走了,显然读取成功了,所以不会再从终端读取!其实这里的10恰好是回车符!这就是为什么这个程序只执行了一次输入操作就结束的原因!
【解决办法】:
清空缓冲区的残留数据。
使用 fflush(stdin); 或 rewind(stdin); 均可起到清空键盘缓冲区的作用,这两个函数均包含在stdio.h这个头文件中
修正后的写法:
Sample two
上面的实例只适用于Windows系统,在Linux环境下上面两种写法都是不起作用的,所以还要换个函数。
Sample three
----
source:http://zhidao.baidu.com/question/191469857.html?fr=qrl&cid=866&index=1&fr2=query
fflush()函数是标准的作法。setbuf(stdin,NULL)是GCC下可用的一种方法。
scanf("%*[^\n]%*c")是用扫描集将缓冲区中的字符全部读取来实现清除缓冲区的动作。
我最近在ubuntu下写一个程序,涉及到了这方面的问题。用fflush()和setbuf(stdin,NULL)都没有解决掉缓存的问题。 (奇怪的错误,DT地不想调试。)后来,搜到有 scanf("%*[^\n]%*c") 这种方法。非常管用,而且还跨平台。
scanf("%*[^\n]%*c")解释:%*〔^\n〕将逐个读取缓冲区中的'\n'字符之前的其它字符,%后面的*表示将读取的这些字符丢弃,前遇到'\n'字符时便停止读取操作,此时,缓冲区中尚有一个'\n'字符遗留,所以后面的%*c将读取并丢弃这个遗留的换行符,这里的星号和前面的星号作用相同。由于所有从键盘的输入都是以回车结束的,而回车会产生一个'\n'字符,所以将'\n'连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区。
C语言中有几个基本输入函数:
//获取字符系列
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);//获取行系列
char *fgets(char * restrict s, int n, FILE * restrict stream);char *gets(char *s);//可能导致溢出,用fgets代替之。//格式化输入系列
int fscanf(FILE * restrict stream, const char * restrict format, …);int scanf(const char * restrict format, …);int sscanf(const char * restrict str, const char * restrict format, …);
这里仅讨论输入函数在标准输入(stdin)情况下的使用。纵观上述各输入函数,
MINGW 4.4.3中FILE结构体源码:
typedef struct _iobuf{char* _ptr;//指向当前缓冲区读取位置int _cnt;//缓冲区中剩余数据长度char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
各编译器实现可能不一样,这里获取字符系列函数只用到_ptr和_cnt。
MINGW 4.4.3中getchar()实现:
__CRT_INLINE int __cdecl __MINGW_NOTHROW getchar (void){return (--stdin->_cnt >= 0)
? (int) (unsigned char) *stdin->_ptr++: _filbuf (stdin);}
其中stdin为FILE指针类型,在MINGW 4.4.3中,getc()和getchar()实现为内联函数,fgetc()实现为函数。顺便说一句,C99标准中已经加入对内联函数的支持了。
=================分 割 线=================
提到缓冲区,就不得不提setbuf和setvbuf两个缓冲区设置函数,其声明如下:
void setbuf(FILE * restrict stream, char * restrict buf);int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);
setvbuf的mode参数有:
setbuf(stream, buf);在:
注:BUFSIZ宏在stdio.h中定义。
这里还要提一下传说中的setbuf的经典错误,在《C陷阱和缺陷》上有提到:
int main()
{int c;
char buf[BUFSIZ];
setbuf(stdout,buf);while((c = getchar()) != EOF)
putchar(c);return 0;
}
问题是这样的:程序交回控制给操作系统之前C运行库必须进行清理工作,其中一部分是刷新输出缓冲,但是此时main函数已经运行完毕,buf缓冲区作用域在main函数中,此时buf字符数组已经释放,导致输出诡异乱码。
解决方案:可以将buf设置为static,或者全局变量,或者调用malloc来动态申请内存。
=================分 割 线=================
下面来看看几种流行的缓冲区清空方法:
由C99标准文档中:
If stream points to an output stream or an update stream in which the most recentoperation was not input, the fflush function causes any unwritten data for that streamto be delivered to the host environment to be written to the file; otherwise, the behavior isundefined.
可以看出fflush对输入流为参数的行为并未定义。但由MSDN上的fflush定义:
If the file associated with stream is open for output, fflush writes to that file thecontents of the buffer associated with the stream. If the stream is open for input,fflush clears the contents of the buffer.
可以看出fflush(stdin)在VC上还是有效地!鉴于各编译器对fflush的未定义行为实现不一样,不推荐使用fflush(stdin)刷新输入缓冲区。
由前面对setbuf函数的介绍,可以得知,setbuf(stdin, NULL);是使stdin输入流由默认缓冲区转为无缓冲区。都没有缓冲区了,当然缓冲区数据残留问题会解决。但这并不是我们想要的。
这里用到了scanf格式化符中的“*”,即赋值屏蔽;“%[^集合]”,匹配不在集合中的任意字符序列。这也带来个问题,缓冲区中的换行符’\n’会留下来,需要额外操作来单独丢弃换行符。
由代码知,不停地使用getchar()获取缓冲区中字符,直到 获取的字符c是换行符’\n’或者是文件结尾符EOF为止。这个方法可以完美清除输入缓冲区,并且具备可移植性。int c;
while((c = getchar()) != '\n' && c != EOF);