C 清空輸入緩沖區,以及fflush(stdin)的使用誤區和解決方法


轉載:https://blog.csdn.net/Veniversum/article/details/62048870

 

對C 語言初學者來說,fflush(stdin)函數被解釋為會清空輸入緩沖區的一個系統函數,這是一個曾經幾乎對過一半的說法,隨着計算機科學的進步,在學習的過程中的逐步完善,將fflush(stdin)函數的過去與現在分析一下。


Personal thinking:

fflush(stdin) 會清空輸入緩沖區中的內容,讀取時輸入緩沖區中的內容會被scanf函數逐個取走,正常case下scanf()函數可以根據返回值判斷成功取走的數目;當發生異常讀取的時候,如應該讀取一個整形,結果輸入緩沖區內當前的內容是個字符串,發生讀取異常。發生讀取異常之后,輸入緩沖區中的內容並未被取走,那么下次循環之時,scanf()函數發現輸入緩沖區中有內容(顯然編譯器不會關心這個內容是不是合法),於是不再等待user輸入,直接嘗試讀取輸入緩沖區中的內容,顯而易見的又是一次讀取異常,如此反復。

include <stdio.h>
int main( void ) { int val,ret; while(fflush(stdin),(ret = scanf("%d",&val)) != EOF) printf(“%d\n”, val); return 0; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

按照直覺,這個程序使用了fflush(stdin)以確保讀取異常時不干涉下一次的讀取。那么問題來了,事實果真如此么?

fflush(stdin)的前世今生
由Microsoft官方提供的MSDN 文檔里也清楚地寫着:fflush on input stream is an extension to the C standard(fflush 操作輸入流是對 C 標准的擴充,注意啊,是Extension!)。即C 標准中根本沒有定義 fflush(stdin),最新的C 11直接刪去了曾經打擦邊球的fflush(stdin)。
這也就是開頭說的fflush(stdin)曾經幾乎對過一半的原因:fflush()確實也是擴展,這就是幾乎對過的意思,在vs 2013之前的版本里,包括 vc++ 6.0 fflush(stdin)也確實管用。現在即使在vs 2015環境下fflush(stdin)也不再起作用,遑論lunix系統下呢,這就是曾經對一半。
當然,如果毫不在乎程序的移植性,在可用 fflush(stdin)的版本里面這么寫也沒什么大問題。


清空輸入緩沖區的可替代方法

既然fflush(stdin)現在在vs 2015 下不干活了,那么總得有接替背鍋的角色,實現清空緩沖區的角色,下面根據查閱的結果,給出兩種在C 可以實現清空輸入緩沖區功能的可行方案。
首先聲明下,使用setbuf(stdin,NULL)是GCC下可用的一種方法,但是沒有解決掉緩存的問題,然而這里不予深究。
在vs 2015 下,可以用下面兩種方法代替fflush(stdin)實現功能:
1)使用函數rewind(stdin)
從函數名上來看,這個函數應該是重定義了輸入緩沖區的Location or Size,用這個方法得到新緩沖區,前后兩個輸入緩沖區並不是一樣的(純猜測,輕點打臉!),其實這個函數好像也是非標准定義的(不是很確定,因為在 C 11與 C 99 的 更新里面真沒看到這個,不過也可以作為暫時管用的半個。都說到這里,多說一句,C 11 標准確實地刪除了gets()函數,用gets_s()代替,關於這一點放在最后一個鏈接內)

2)使用scanf(“%*[^\n]%*c”),原理是用掃描集將緩沖區中的字符全部讀取來實現清除輸入緩沖區的動作,就效果來說非常管用,而且還跨平台。
乍一看這東西還有點深奧,給出詳細的解釋,也可以參考鏈接里面的解釋說明。
對scanf(“%*[^\n]%*c”)解釋:
〔^\n〕將逐個讀取緩沖區中的’\n’字符之前的其它字符,%后面的表示將讀取的這些字符丟棄,前遇到’\n’字符時便停止讀取操作,此時,緩沖區中尚有一個’\n’字符遺留,所以后面的%*c將讀取並丟棄這個遺留的換行符,這里的星號和前面的星號作用相同。由於所有從鍵盤的輸入都是以回車結束的,而回車會產生一個’\n’字符,所以將’\n’連同它之前的字符全部讀取並丟棄之后,也就相當於清除了輸入緩沖區。


關於fflush(stdout)

如果不考慮fflush(stdin)這個坑的話,它的兄弟fflush(stdout)還是有很大的作用的,簡言之,fflush(stdout)強制輸出當前輸出緩沖區中的內容,一些在Debug下一些莫名其妙的error可以用fflush(stdout)立即輸出在處理過程中的中間結果來確定error所在。
在查閱過程中發現一句話:
fflush(stdin)對輸入流的操作是未定義的,所以這個還是要慎用,或許有副作用。
fflush(stdout)只是將需要輸出的輸出緩沖區中內容當即輸出,利於調試且沒有什么不良后果。

不管怎么說,向當年的二極管一樣,fflush(stdin)也在計算機的發展過程中廣泛使用過,時過境遷。一回首已百年身,也許以后的學習過程中再也不會遇見fflush(stdin)了。


參考資料:

1.ISO/IEC 9899:1999 (E) Programming languages— C 7.19.5.2 The fflush function
2.The C Programming Language 2nd Edition By Kernighan & Ritchie


相關鏈接:

scanf(“%[^\n]%*c”)怎么理解
scanf與緩存
C語言格式輸入函數scanf()詳解
用rewind(stdin)代替fflush(stdin)
C語言標准中的C99與最新的C11


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM