1.為什麼 fflush(stdin)是錯的
首先請看以下程序:
include <stdio.h>
int main( void )
{
int i;
for ( ;;) {
fputs("Please input an integer: ", stdout);
scanf("%d", &i);
printf("%d\n", i);
}
return 0;
}
這個程序首先會提示用戶輸入一個整數,然後等 待用戶輸入,如果用戶輸入的是整數,程序會輸出剛才輸入的整數,並且再次提 示用戶輸入一個整數,然後等待用戶輸入。但是一旦用戶輸入的不是整數(如小 數或者字母),假設 scanf 函數最後一次得到的整數是 2 ,那麼程序會不停地 輸出“Please input an integer: 2”。這是因為 scanf ("%d", &i); 只能接受整數,如果用戶輸入了字母,則這個 字母會遺留在“輸入緩沖區”中。因為緩沖中有數據,故而 scanf 函數不會等待用戶輸入,直接就去緩沖中讀取,可是緩沖中的卻是字母,這個字 母再次被遺留在緩沖中,如此反復,從而導致不停地輸出“Please input an integer: 2”。
也許有人會說:“居然這樣,那麼在 scanf 函數後面加上‘fflush(stdin);’,把輸入緩沖清空掉不 就行了?”然而這是錯的!C和C++的標准裡從來沒有定義過 fflush (stdin)。也許有人會說:“可是我用 fflush(stdin) 解決了這個問 題,你怎麼能說是錯的呢?”的確,某些編譯器(如VC6)支持用 fflush (stdin) 來清空輸入緩沖,但是並非所有編譯器都要支持這個功能(linux 下 的 gcc 就不支持),因為標准中根本沒有定義 fflush(stdin)。MSDN 文檔裡 也清楚地寫著fflush on input stream is an extension to the C standard( fflush 操作輸入流是對 C 標准的擴充)。當然,如果你毫不在乎程序的移植性 ,用 fflush(stdin) 也沒什麼大問題。以下是 C99 對 fflush 函數的定義:
int fflush(FILE *stream);
如果 stream 指向輸出流或者更 新流(update stream),並且這個更新流最近執行的操作不是輸入,那麼 fflush 函數將把這個流中任何待寫數據傳送至宿主環境(host environment) 寫入文件。否則,它的行為是未定義的。
原文如下:
int fflush (FILE *stream);
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 file; otherwise, the behavior is undefined.
其中,宿主環境可以理解為 操作系統或內核等。
由此可知,如果 stream 指向輸入流(如 stdin) ,那麼 fflush 函數的行為是不確定的。故而使用 fflush(stdin)是不正確的 ,至少是移植性不好的。
2. 清空輸入緩沖區的方法
雖然不可以用 fflush(stdin),但是我 們可以自己寫代碼來清空輸入緩沖區。只需要在 scanf 函數後面加上幾句簡單 的代碼就可以了。
/* C 版本 */
#include <stdio.h>
int main( void )
{
int i, c;
for ( ; ; )
{
fputs("Please input an integer: ", stdout);
scanf("%d", &i);
if ( feof(stdin) || ferror(stdin) )
{ /* 如果用戶輸入文件結束標志(或 文件已被讀完), */
/* 或者發生讀寫錯誤,則退出循環 */
/* do something */
break;
}
/* 沒有發生錯誤,清空輸入流。 */
/* 通過 while 循環把輸入流中的余留數據“吃”掉 */
while ( (c = getchar()) != '\n' && c != EOF ) ;
/* 使用 scanf("%*[^\n]"); 也可以清空輸入流, */
/* 不過會殘留 \n 字符。*/
printf("%d\n", i);
}
return 0;
}
/* C++ 版本 */
#include <iostream>
#include <limits> // 為了 使用numeric_limits
using std::cout;
using std::endl;
using std::cin;
using std::numeric_limits;
using std::streamsize;
int main()
{
int value;
for ( ; ; )
{
cout << "Enter an integer: ";
cin >> value;
if ( cin.eof() || cin.bad() )
{ // 如果用戶輸入文件結束標志(或文件已被讀完),
// 或者發生讀寫錯誤,則退出循環
// do something
break;
}
// 讀到非法字符後,輸入流將處於出錯狀態,
// 為了繼續獲取輸入,首先要調用 clear 函數
// 來清除輸入流的錯誤標記 ,然後才能調用
// ignore 函數來清除輸入流中的數據。
cin.clear();
// numeric_limits<streamsize>::max() 返回輸 入緩沖的大小。
// ignore 函數在此將把輸入流中的數據清空。
// 這兩個函數的具體用法請讀者自行查詢。
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
cout << value << '\n';
}
return 0;
}