關於 fflush 函數(stdin,stdout)

寫在最前面

在別人的程序裏看到這個函數,本着學習的態度整理一番。linux

p.s. 新添加一些內容沒,我從 關於fflush(stdin)清空輸入緩存流(C/C++) 新手必看!!這裏看來的,對理解緩衝區有很大幫助。ios

fflush(stdout)

在printf()後使用fflush(stdout)的做用是馬上將要輸出的內容輸出。
當使用printf()函數後,系統將內容存入輸出緩衝區,等到時間片輪轉到系統的輸出程序時,將其輸出。
使用fflush(out)後,馬上清空輸出緩衝區,並把緩衝區內容輸出。
例如:c++

for (ctr = 1; ctr <= wait; ctr++)
{
printf("."); /* print a dot */
fflush(stdout); /* force dot to print on buffered machines */
sleep((int) 1); /* pause 1 second */
}

用fflush(stdout)能使機器每輸出一個.暫停一秒鐘,而不會出現亂序(例如:PP。。。PP。PPP。。。等)現象。緩存

一個經典的例子:函數

#include <stdio.h>  
int main()  
{  
    char c;  
    scanf("%c", &c);  
    printf("%d\n", c);  
  
    //fflush(stdin); // 沖掉「馬桶」中的無用值  
    
    scanf("%c", &c);  
    printf("%d\n", c);  
      
    return 0;  
  
}

若是輸入1後回車,輸出的是49和10,很正常,由於回車就是在ASCii中就是49。學習

fflush(stdin)

功能:清空輸入緩衝區,一般是爲了確保不影響後面的數據讀取(例如在讀完一個字符串後緊接着又要讀取一個字符,此時應該先執行fflush(stdin);)。.net

  • 這裏給你們提個醒,爲何用fflush(stdin)是錯的?code

先看下下面的程序blog

#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」。ci

這是由於 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) 也沒什麼大問題。

  • 那麼爲了移植性以及程序的魯棒性,咱們應該怎麼作?

清空輸入緩衝區的方法

/* 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 ) ;    /*可直接將這句代碼當成fflush(stdio)的替代,直接運行可清除輸入緩存流*/
        /* 使用 scanf("%*[^\n]"); 也能夠清空輸入流, */
        /* 不過會殘留 \n 字符。                          */

        printf("%d\n", i);
     }

     return 0;
   }

上面是c版本的,下面是c++版本的。

/* 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;
   }

我的以爲這兩個版本寫的不錯,能夠添加到本身的私有庫裏。根據這個原理本身還能夠改寫一些別的版本。

總結

c99對這個函數的定義:

int fflush(FILE*stream);
若是stream指向輸出流或者更新流(update stream),而且這個更新流
最近執行的操做不是輸入,那麼fflush函數將把任何未被寫入的數據寫入stream
指向的文件(如標準輸出文件stdout)。不然,fflush函數的行爲是不肯定的。
fflush(NULL)清空全部輸出流和上面提到的更新流。若是發生寫錯誤,fflush
函數會給那些流打上錯誤標記,而且返回EOF,不然返回0。

這個函數仍是頗有用的,能夠加深對緩存的理解。沒有想到一個輸入都有這麼多須要關注的東西,真的是c++是學一生的東西!-_-

相關文章
相關標籤/搜索