您的位置:首页 > 编程语言 > C语言/C++

C语言清空输入缓冲区的N种方法对比

2013-07-19 02:14 417 查看
C语言中有几个基本输入函数:
//获取字符系列
int fgetc(FILE *stream);[/code]
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)情况下的使用。纵观上述各输入函数,
获取字符系列的的前三个函数fgetc、getc、getchar。以getchar为例,将在stdin缓冲区为空时,等待输入,直到回车换行时函数返回。若stdin缓冲区不为空,getchar直接返回。getchar返回时从缓冲区中取出一个字符,并将其转换为int,返回此int值。MINGW 4.4.3中FILE结构体源码
typedef struct _iobuf
{[/code]
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)
{[/code]
return (--stdin->_cnt >= 0)
?  (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}
其中stdin为FILE指针类型,在MINGW 4.4.3中,getc()和getchar()实现为内联函数,fgetc()实现为函数。顺便说一句,C99标准中已经加入对内联函数的支持了。获取行系列的fgets和gets,其中由于gets无法确定缓冲区大小,常导致溢出情况,这里不推荐也不讨论gets函数。对于fgets函数,每次敲入回车,fgets即返回。fgets成功返回时,将输入缓冲区中的数据连换行符’\n’一起拷贝到第一个参数所指向的空间中。若输入数据超过缓冲区长度,fgets会截取数据到前n-1(n为fgets第二个参数,为第一个参数指向空间的长度),然后在末尾加入’\n’。因此fgets是安全的。通常用fgets(buf,BUF_LEN, stdin);代替gets(buf);。格式化输入系列中,fscanf从文件流进行格式化输入很不好用。常用的还是scanf,格式化输入系列函数舍去输入数据(根据函数不同可能是标准输入也可能是字符串输入,如:sscanf)前的空白字符(空格、制表符、换行符)直至遇到非空白字符,然后根据格式参数尝试对非空白字符及后续字符进行解析。该系列函数返回成功解析赋值的变量数,若遇文件尾或错误,返回EOF。=================分 割 线=================提到缓冲区,就不得不提setbufsetvbuf两个缓冲区设置函数,其声明如下:
void setbuf(FILE * restrict stream, char * restrict buf);
int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);[/code]setvbuf的mode参数有:_IOFBF(满缓冲):缓冲区空时读入数据;缓冲区满时向流写入数据。_IOLBF(行缓冲):每次从流读入一行数据或向流写入数据。如:stdio,stdout_IONBF(无缓冲):直接从流读入数据,或者直接向流写入数据,而没有缓冲区。如:stderrsetbuf(stream, buf);在:buf == NULL:等价于(void)setvbuf(stream, NULL, _IONBF, 0);buf指向长度为BUFSIZ的缓冲区:等价于(void)setvbuf(stream, buf, _IOFBF, BUFSIZ);注:BUFSIZ宏在stdio.h中定义。这里还要提一下传说中的setbuf经典错误,在《C陷阱和缺陷》上有提到:
int main()
{[/code]
int c;
char buf[BUFSIZ];
setbuf(stdout,buf);
while((c = getchar()) != EOF)
putchar(c);
    return 0;
}
问题是这样的:程序交回控制给操作系统之前C运行库必须进行清理工作,其中一部分是刷新输出缓冲,但是此时main函数已经运行完毕,buf缓冲区作用域在main函数中,此时buf字符数组已经释放,导致输出诡异乱码。解决方案:可以将buf设置为static,或者全局变量,或者调用malloc来动态申请内存。=================分 割 线=================下面来看看几种流行的缓冲区清空方法:fflush(stdin);式由C99标准文档中:
If stream points to an output stream or an update stream in which the most recent
operation was not input, the fflush function causes any unwritten data for that stream[/code]
to be delivered to the host environment to be written to the file; otherwise, the behavior is
undefined.
可以看出fflush对输入流为参数的行为并未定义。但由MSDN上的fflush定义:
If the file associated with stream is open for output, fflush writes to that file the
contents of the buffer associated with the stream. If the stream is open for input,[/code]
fflush clears the contents of the buffer.
可以看出fflush(stdin)在VC上还是有效地!鉴于各编译器对fflush的未定义行为实现不一样,不推荐使用fflush(stdin)刷新输入缓冲区。setbuf(stdin, NULL);式由前面对setbuf函数的介绍,可以得知,setbuf(stdin, NULL);是使stdin输入流由默认缓冲区转为无缓冲区。都没有缓冲区了,当然缓冲区数据残留问题会解决。但这并不是我们想要的。scanf("%*[^\n]");式(《C语言程序设计 现代方法 第二版》中提到)这里用到了scanf格式化符中的“*”,即赋值屏蔽;“%[^集合]”,匹配不在集合中的任意字符序列。这也带来个问题,缓冲区中的换行符’\n’会留下来,需要额外操作来单独丢弃换行符。经典式
int c;
while((c = getchar()) != '\n' && c != EOF);[/code]由代码知,不停地使用getchar()获取缓冲区中字符,直到获取的字符c是换行符’\n’或者是文件结尾符EOF为止。这个方法可以完美清除输入缓冲区,并且具备可移植性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: