1. 程式人生 > >c++ 中緩衝區的理解

c++ 中緩衝區的理解

什麼是緩衝區

緩衝區又稱為快取,它是記憶體空間的一部分。也就是說,在記憶體空間中預留了一定的儲存空間,這些儲存空間用來緩衝輸入或輸出的資料,這部分預留的空間就叫做緩衝區。緩衝區根據其對應的是輸入裝置還是輸出裝置,分為輸入緩衝區和輸出緩衝區。

為什麼要引入緩衝區

我們為什麼要引入緩衝區呢?

比如我們從磁盤裡取資訊,我們先把讀出的資料放在緩衝區,計算機再直接從緩衝區中取資料,等緩衝區的資料取完後再去磁碟中讀取,這樣就可以減少磁碟的讀寫次數,再加上計算機對緩衝區的操作大大快於對磁碟的操作,故應用緩衝區可大大提高計算機的執行速度。

又比如,我們使用印表機列印文件,由於印表機的列印速度相對較慢,我們先把文件輸出到印表機相應的緩衝區,印表機再自行逐步列印,這時我們的CPU可以處理別的事情。現在您基本明白了吧,緩衝區就是一塊記憶體區,它用在輸入輸出裝置和CPU之間,用來快取資料。它使得低速的輸入輸出裝置和高速的CPU能夠協調工作,避免低速的輸入輸出裝置佔用CPU,解放出CPU,使其能夠高效率工作。

緩衝區的型別

緩衝區 分為三種類型:全緩衝、行緩衝和不帶緩衝。

1、全緩衝

在這種情況下,當填滿標準I/O快取後才進行實際I/O操作。全緩衝的典型代表是對磁碟檔案的讀寫。

2、行緩衝

在這種情況下,當在輸入和輸出中遇到換行符時,執行真正的I/O操作。這時,我們輸入的字元先存放在緩衝區,等按下回車鍵換行時才進行實際的I/O操作。典型代表是鍵盤輸入資料。

3、不帶緩衝

也就是不進行緩衝,標準出錯情況stderr是典型代表,這使得出錯資訊可以直接儘快地顯示出來。
緩衝區的重新整理

下列情況會引發緩衝區的重新整理:

1、緩衝區滿時;

2、執行flush語句;

3、執行endl語句;

4、關閉檔案。

可見,緩衝區滿或關閉檔案時都會重新整理緩衝區,進行真正的I/O操作。另外,在C++中,我們可以使用flush函式來重新整理緩衝區(執行I/O操作並清空緩衝區),如:cout << flush; //將視訊記憶體的內容立即輸出到顯示器上進行顯示

endl控制符的作用是將游標移動到輸出裝置中下一行開頭處,並且清空緩衝區。

cout < < endl;

相當於

cout < < ”\n”< < flush;

通過例項演示說明

1、檔案操作演示全緩衝

建立一個控制檯工程,輸入如下程式碼:

[cpp] view plaincopyprint?
  1. #include< fstream >
  2. usingnamespace std;  
  3. int main()  
  4. {  
  5. //建立檔案test.txt並開啟
  6.     ofstream outfile("test.txt");  
  7. //向test.txt檔案中寫入4096個字元’a’
  8. for(int n=0;n< 4096;n++)  
  9.     {  
  10.         outfile <<  'a';  
  11.     }  
  12. //暫停,按任意鍵繼續
  13.     system("PAUSE");  
  14. //繼續向test.txt檔案中寫入字元’b’,也就是說,第4097個字元是’b’
  15.     outfile < <  'b';  
  16. //暫停,按任意鍵繼續
  17.     system("PAUSE");  
  18. return 0;  
  19. }  
#include< fstream >
using namespace std;

int main()
{
    //建立檔案test.txt並開啟
	ofstream outfile("test.txt");

    //向test.txt檔案中寫入4096個字元’a’
	for(int n=0;n< 4096;n++)
	{
		outfile <<  'a';
	}
    //暫停,按任意鍵繼續
	system("PAUSE");
    
    //繼續向test.txt檔案中寫入字元’b’,也就是說,第4097個字元是’b’
	outfile < <  'b';

    //暫停,按任意鍵繼續
	system("PAUSE");

	return 0;
}

上面這段程式碼很容易理解,已經在程式碼內部作了註釋。

編寫這段小程式碼的目的是驗證全緩衝的大小是4096個位元組,並驗證緩衝區滿後會重新整理緩衝區,執行真正的I/O操作。

編譯並執行,執行結果如下:


此時開啟工程所在資料夾下的test.txt檔案,您會發現該檔案是空的,這說明4096個字元“a”還在緩衝區,並沒有真正執行I/O操作。敲一下回車鍵,視窗變為如下:


此時再開啟test.txt檔案,您就會發下該檔案中已經有了4096個字元“a”。這說明全緩衝區的大小是4K(4096),緩衝區滿後執行了I/O操作,而字元“b”還在緩衝區。
再次敲一下回車鍵,視窗變為如下:


此時再開啟test.txt檔案,您就會發現字元“b”也在其中了。這一步驗證了檔案關閉時重新整理了緩衝區。

2、鍵盤操作演示行緩衝

先介紹getchar()函式。

函式原型:int getchar(void);

說明:當程式呼叫getchar()函式時,程式就等著使用者按鍵,使用者輸入的字元被存放在鍵盤緩衝區中,直到使用者按回車為止(回車字元也放在緩衝區中)。當用戶鍵入回車之後,getchar()函式才開始從鍵盤緩衝區中每次讀入一個字元。也就是說,後續的getchar()函式呼叫不會等待使用者按鍵,而直接讀取緩衝區中的字元,直到緩衝區中的字元讀完後,才重新等待使用者按鍵。

不知道您明白了沒有,再通俗一點講,當程式呼叫getchar()函式時,程式就等著使用者按鍵,並等使用者按下回車鍵返回。期間按下的字元存放在緩衝區,第一個字元作為函式返回值。繼續呼叫getchar()函式,將不再等使用者按鍵,而是返回您剛才輸入的第2個字元;繼續呼叫,返回第3個字元,直到緩衝區中的字元讀完後,才等待使用者按鍵。

如果您還沒有明白,只能怨我表達能力有限,您可以結合以下例項體會。

建立一個控制檯工程,輸入如下程式碼:

[cpp] view plaincopyprint?
  1. #include < iostream >
  2. usingnamespace std;  
  3. int main()  
  4. {  
  5. char c;  
  6. //第一次呼叫getchar()函式
  7. //程式執行時,您可以輸入一串字元並按下回車鍵,按下回車鍵後該函式才返回
  8.     c=getchar();  
  9. //顯示getchar()函式的返回值
  10.     cout < <  c < < endl;  
  11. //暫停
  12.     system("PAUSE");  
  13. //迴圈多次呼叫getchar()函式
  14. //將每次呼叫getchar()函式的返回值顯示出來
  15. //直到遇到回車符才結束
  16. while((c=getchar())!='\n')  
  17.     {  
  18.         printf("%c",c);  
  19.     }  
  20. //暫停
  21.     system("PAUSE");  
  22. return 0;  
  23. }  
#include < iostream >
using namespace std;

int main()
{

	char c;

//第一次呼叫getchar()函式
//程式執行時,您可以輸入一串字元並按下回車鍵,按下回車鍵後該函式才返回
	c=getchar();

    //顯示getchar()函式的返回值
	cout < <  c < < endl;

    //暫停
	system("PAUSE");
 
//迴圈多次呼叫getchar()函式
//將每次呼叫getchar()函式的返回值顯示出來
//直到遇到回車符才結束
	while((c=getchar())!='\n')
	{
		printf("%c",c);
	}

    //暫停
	system("PAUSE");

	return 0;
}

這段小程式碼也很簡單,同樣在程式碼內部都有註釋。

getchar()函式的執行就是採用了行緩衝。第一次呼叫getchar()函式,會讓程式使用者(使用者)輸入一行字元並直至按下回車鍵 函式才返回。此時使用者輸入的字元和回車符都存放在行緩衝區。

再次呼叫getchar()函式,會逐步輸出行緩衝區的內容。

好了,本人表達能力有限,還是編譯執行程式,通過執行結果自己領會吧。

編譯執行程式,會提示您輸入字元,您可以交替按下一些字元,如下:


您一直按下去,您就會發現當您按到第4094個字元時,不允許您繼續輸入字元。這說明行緩衝區的大小也是4K。

此時您按下回車鍵,返回第一個字元’a’,如下圖:


繼續敲一下回車鍵,將緩衝區的其它的字元全部輸出,如下圖:


3、標準錯誤輸出不帶緩衝

如錯誤輸出時使用:

cerr<<”錯誤,請檢查輸入的引數!”;

這條語句等效於:

fprintf(stderr, ”錯誤,請檢查輸入的引數!”);