溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

使用C語言清空輸入緩沖區(qū)的方法

發(fā)布時(shí)間:2020-09-29 16:32:29 來源:億速云 閱讀:194 作者:小新 欄目:編程語言

這篇文章主要介紹使用C語言清空輸入緩沖區(qū)的方法,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

 C語言中有幾個(gè)基本輸入函數(shù):

//獲取字符系列
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);//可能導(dǎo)致溢出,用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, …);

這里僅討論輸入函數(shù)在標(biāo)準(zhǔn)輸入(stdin)情況下的使用??v觀上述各輸入函數(shù),

  • 獲取字符系列的的前三個(gè)函數(shù)fgetc、getc、getchar。以getchar為例,將在stdin緩沖區(qū)為空時(shí),等待輸入,直到回車換行時(shí)函數(shù)返回。若stdin緩沖區(qū)不為空,getchar直接返回。getchar返回時(shí)從緩沖區(qū)中取出一個(gè)字符,并將其轉(zhuǎn)換為int,返回此int值。

MINGW 4.4.3中FILE結(jié)構(gòu)體源碼

  _iobuf
{
	char*	_ptr;//指向當(dāng)前緩沖區(qū)讀取位置
	int	_cnt;//緩沖區(qū)中剩余數(shù)據(jù)長(zhǎng)度
	char*	_base;
	int	_flag;
	int	_file;
	int	_charbuf;
	int	_bufsiz;
	char*	_tmpfname;
} FILE;

各編譯器實(shí)現(xiàn)可能不一樣,這里獲取字符系列函數(shù)只用到_ptr和_cnt。

MINGW 4.4.3中g(shù)etchar()實(shí)現(xiàn)

__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()實(shí)現(xiàn)為內(nèi)聯(lián)函數(shù),fgetc()實(shí)現(xiàn)為函數(shù)。順便說一句,C99標(biāo)準(zhǔn)中已經(jīng)加入對(duì)內(nèi)聯(lián)函數(shù)的支持了。

  • 獲取行系列的fgets和gets,其中由于gets無法確定緩沖區(qū)大小,常導(dǎo)致溢出情況,這里不推薦也不討論gets函數(shù)。對(duì)于fgets函數(shù),每次敲入回車,fgets即返回。fgets成功返回時(shí),將輸入緩沖區(qū)中的數(shù)據(jù)連換行符’\n’一起拷貝到第一個(gè)參數(shù)所指向的空間中。若輸入數(shù)據(jù)超過緩沖區(qū)長(zhǎng)度,fgets會(huì)截取數(shù)據(jù)到前n-1(n為fgets第二個(gè)參數(shù),為第一個(gè)參數(shù)指向空間的長(zhǎng)度),然后在末尾加入’\n’。因此fgets是安全的。通常用fgets(buf, BUF_LEN, stdin);代替gets(buf);。

  • 格式化輸入系列中,fscanf從文件流進(jìn)行格式化輸入很不好用。常用的還是scanf,格式化輸入系列函數(shù)舍去輸入數(shù)據(jù)(根據(jù)函數(shù)不同可能是標(biāo)準(zhǔn)輸入也可能是字符串輸入,如:sscanf)前的空白字符(空格、制表符、換行符)直至遇到非空白字符,然后根據(jù)格式參數(shù)嘗試對(duì)非空白字符及后續(xù)字符進(jìn)行解析。該系列函數(shù)返回成功解析賦值的變量數(shù),若遇文件尾或錯(cuò)誤,返回EOF。

=================分 割 線=================

提到緩沖區(qū),就不得不提setbufsetvbuf兩個(gè)緩沖區(qū)設(shè)置函數(shù),其聲明如下:

 setbuf(FILE * restrict stream,  * restrict buf);
int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);

setvbuf的mode參數(shù)有:

  • _IOFBF(滿緩沖):緩沖區(qū)空時(shí)讀入數(shù)據(jù);緩沖區(qū)滿時(shí)向流寫入數(shù)據(jù)。

  • _IOLBF(行緩沖):每次從流讀入一行數(shù)據(jù)或向流寫入數(shù)據(jù)。如:stdio,stdout

  • _IONBF(無緩沖):直接從流讀入數(shù)據(jù),或者直接向流寫入數(shù)據(jù),而沒有緩沖區(qū)。如:stderr

setbuf(stream, buf);在:

  • buf == NULL:等價(jià)于(void)setvbuf(stream, NULL, _IONBF, 0);

  • buf指向長(zhǎng)度為BUFSIZ的緩沖區(qū):等價(jià)于(void)setvbuf(stream, buf, _IOFBF, BUFSIZ);

注:BUFSIZ宏在stdio.h中定義。

這里還要提一下傳說中的setbuf經(jīng)典錯(cuò)誤,在《C陷阱和缺陷》上有提到:

 main()
{
    int c;
    char buf[BUFSIZ];

    setbuf(stdout,buf);
    while((c = getchar()) != EOF)
        putchar(c);
    
    return 0;
}

問題是這樣的:程序交回控制給操作系統(tǒng)之前C運(yùn)行庫必須進(jìn)行清理工作,其中一部分是刷新輸出緩沖,但是此時(shí)main函數(shù)已經(jīng)運(yùn)行完畢,buf緩沖區(qū)作用域在main函數(shù)中,此時(shí)buf字符數(shù)組已經(jīng)釋放,導(dǎo)致輸出詭異亂碼。

解決方案:可以將buf設(shè)置為static,或者全局變量,或者調(diào)用malloc來動(dòng)態(tài)申請(qǐng)內(nèi)存。

=================分 割 線=================

下面來看看幾種流行的緩沖區(qū)清空方法:

  • fflush(stdin);式

由C99標(biāo)準(zhǔn)文檔中:

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
to be delivered to the host environment to be written to the ?le; otherwise, the behavior is
unde?ned.

可以看出fflush對(duì)輸入流為參數(shù)的行為并未定義。但由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,
fflush clears the contents of the buffer.

可以看出fflush(stdin)在VC上還是有效地!鑒于各編譯器對(duì)fflush的未定義行為實(shí)現(xiàn)不一樣,不推薦使用fflush(stdin)刷新輸入緩沖區(qū)。

  • setbuf(stdin, NULL);式

由前面對(duì)setbuf函數(shù)的介紹,可以得知,setbuf(stdin, NULL);是使stdin輸入流由默認(rèn)緩沖區(qū)轉(zhuǎn)為無緩沖區(qū)。都沒有緩沖區(qū)了,當(dāng)然緩沖區(qū)數(shù)據(jù)殘留問題會(huì)解決。但這并不是我們想要的。

  • scanf("%*[^\n]");式(《C語言程序設(shè)計(jì) 現(xiàn)代方法 第二版》中提到)

這里用到了scanf格式化符中的“*”,即賦值屏蔽;“%[^集合]”,匹配不在集合中的任意字符序列。這也帶來個(gè)問題,緩沖區(qū)中的換行符’\n’會(huì)留下來,需要額外操作來單獨(dú)丟棄換行符。

  • 經(jīng)典式

 c;
while((c = getchar()) != '\n' && c != EOF);

由代碼知,不停地使用getchar()獲取緩沖區(qū)中字符,直到獲取的字符c是換行符’\n’或者是文件結(jié)尾符EOF為止。這個(gè)方法可以完美清除輸入緩沖區(qū),并且具備可移植性。

以上是使用C語言清空輸入緩沖區(qū)的方法的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI