緩衝區是為了讓低速的輸入輸出裝置和高速的使用者程式能夠協調工作,並降低輸入輸出裝置的讀寫次數。
使用者程式的執行速度可以看做 cpu 的執行速度,如果沒有各種硬體的阻礙,理論上它們是同步的。例如,我們都知道硬碟的速度要遠低於 cpu,它們之間有好幾個數量級的差距,當向硬碟寫入資料時,程式需要等待,不能做任何事情,就好像卡頓了一樣,使用者體驗非常差。計算機上絕大多數應用程式都需要和硬體打交道,例如讀寫硬碟、向顯示器輸出、從鍵盤輸入等,如果每個程式都等待硬體,那麼整台計算機也將變得卡頓。
但是有了緩衝區,就可以將資料先放入緩衝區中(記憶體的讀寫速度也遠高於硬碟),然後程式可以繼續往下執行,等所有的資料都準備好了,再將緩衝區中的所有資料一次性地寫入硬碟,這樣程式就減少了等待的次數,變得流暢起來。
緩衝區的另外乙個好處是可以減少硬體裝置的讀寫次數。其實我們的程式並不能直接讀寫硬體,它必須告訴作業系統,讓作業系統核心(kernel)去呼叫驅動程式,只有驅動程式才能真正的操作硬體。
從使用者程式到硬體裝置要經過好幾層的轉換,每一層的轉換都有時間和空間的開銷,而且開銷不一定小;一旦使用者程式需要密集的輸入輸出操作,這種開銷將變得非常大,會成為制約程式效能的瓶頸。
這個時候,分配緩衝區就是必不可少的。每次呼叫讀寫函式,先將資料放入緩衝區,等資料都準備好了再進行真正的讀寫操作,這就大大減少了轉換的次數。實踐證明,合理的緩衝區設定能成倍提高程式效能。
現在你基本明白了吧,緩衝區其實就是一塊記憶體空間,它用在硬體裝置和使用者程式之間,用來快取資料,目的是讓快速的 cpu 不必等待慢速的輸入輸出裝置,同時減少操作硬體的次數。
根據不同的標準,緩衝區可以有不同的分類。
根據緩衝區對應的是輸入裝置還是輸出裝置,可以分為輸入緩衝區和輸出緩衝區。
根據資料重新整理(也可以稱為清空緩衝區,就是將緩衝區中的資料「倒出」)的時機,可以分為全緩衝、行緩衝、不帶緩衝。這種分類才本節要重點講解的內容。
1) 全緩衝
在這種情況下,
當緩衝區被填滿以後才進行真正的輸入輸出操作。
緩衝區的大小都有限制的,比如 1kb、4mb 等,資料量達到最大值時就清空緩衝區。
全緩衝的典型代表是對硬碟檔案的讀寫,我們將在《
c語言檔案操作
》一章中深入講解。
在實際開發中,將資料寫入檔案後,開啟檔案並不能立即看到內容,只有清空緩衝區,或者關閉檔案,或者關閉程式後,才能在檔案中看到內容。這種現象,就是緩衝區在作怪。
2) 行緩衝
在這種情況下
,當在輸入或者輸出的過程中遇到換行符時,才執行真正的輸入輸出操作。
行緩衝的典型代表就是標準輸入裝置(也即鍵盤)和標準輸出裝置(也即顯示器)。
① 在講解 printf() 時,我們在 linux 或者 mac os 平台測試了如下的**:
#include
#include
int
main
()
執行程式後,會發現第乙個 printf() 並沒有立即輸出,而是等待 5 秒以後,和第二個 printf() 一起輸出了。
究其原因,就是 printf() 帶有行緩衝區,"c語言中文網"這幾個字元要先放入緩衝區中,而不是立即顯示到螢幕上。放入緩衝區以後,程式又暫停了 5 秒,然後執行第二個 printf(),又將""
放入了緩衝區。注意最後的換行符\n
,它會使得緩衝區重新整理,將緩衝區中的所有內容都輸出到顯示器上,所以我們才看到兩個 printf() 一起輸出。
如果將第乙個 printf() 的最後加上換行符\n
,也就是寫作下面的形式:
printf("c語言中文網\n");
此時情況又不一樣了,第乙個 printf() 會先輸出,第二個 printf() 等待 5 秒以後才輸出。這是因為,第乙個 printf() 的最後有換行符\n
,它會使得緩衝區重新整理,所以立即就輸出了,不用等著第二個 printf()。
② 對於 scanf(),不管使用者輸入多少內容,只要不按下回車鍵,就不進行真正的讀取。
這是因為 scanf() 是帶有行緩衝的
,使用者輸入的內容會先放入緩衝區,直到使用者按下回車鍵,產生換行符\n
,才會重新整理緩衝區,進行真正的讀取。
scanf() 還有很多與緩衝區有關的奇怪問題,在《
使用scanf從鍵盤輸入資料
》一節中相信讀者也領略到了,但是由於本節篇幅有限,在此就不展開討論了,下節《
結合緩衝區談scanf()函式
》我會用一節的內容來深入講解。
3) 不帶緩衝
不帶緩衝區,資料就沒有地方快取,必須立即進行輸入輸出。
getche()、getch() 就不帶緩衝區,輸入乙個字元後立即就執行了,根本不用按下回車鍵。
windows 下的 printf() 也不帶緩衝區,不管最後有沒有換行符\n
,都會立即輸出,
所以對於類似的輸出**,windows 和 linux、mac os 會有不同的表現,這在《
c語言資料輸出大彙總以及輕量高階
》一節中已經進行了演示。
錯誤資訊輸出函式 perror() 也沒有緩衝區。錯誤資訊必須刻不容緩、立即、馬上顯示出來,緩衝區將會增加捕獲錯誤的時間,這是毫無理由的。c語言標準規定,輸入輸出緩衝區要具有以下特徵:
現代計算機已經沒有了專門的錯誤顯示裝置,所有的資訊都顯示到乙個螢幕上,這裡的錯誤顯示裝置只能是計算機的顯示器。上面提到的 perror() 其實就是向錯誤顯示裝置上輸出資訊,但是現代計算機已經把顯示器作為了錯誤顯示裝置,所以 perror() 也是向顯示器上輸出內容。所謂互動裝置,就是現代計算機上的顯示器和鍵盤。c標準雖然規定它們不能是全緩衝的,但並沒有規定它們到底是行緩衝還是不帶緩衝,這就導致不同的平台有不同的實現。
1) 輸入裝置
scanf()、getchar()、gets() 就是從輸入裝置(鍵盤)上讀取內容。對於輸入裝置,沒有緩衝區將導致非常奇怪的行為,比如,我們本來想輸入乙個整數 947,沒有緩衝區的話,輸入 9 就立即讀取了,根本沒有機會輸入 47,所以,沒有輸入緩衝區是不能接受的。windows、linux、mac os 在實現時都給輸入裝置帶上了行緩衝,所以 scanf()、getchar()、gets() 在每個平台下的表現都一致。
但是在某些特殊情況下,我們又希望程式能夠立即響應使用者按鍵,例如在遊戲中,使用者按下方向鍵人物要立即轉向,而且越快越好,這肯定就不能帶有緩衝區了。windows 下特有的 getche() 和 getch() 就是為這種特殊需求而設計的,它們都不帶緩衝區。
2) 輸出裝置
printf()、puts()、putchar() 就是向輸出裝置(顯示器)上顯示內容。對於輸出裝置,有沒有緩衝區其實影響沒有那麼大,頂多是晚一會看到內容,不會有功能性的障礙,所以 windows 和 linux、mac os 採用了不同的方案:
在講解 printf() 時,我們在 windows 平台測試了如下的**:
#include
#include
intmain
()
執行程式後,會發現第乙個 printf() 首先輸出(程式執行後立即輸出),等待 5 秒以後,第二個 printf() 才輸出,第乙個 printf() 不會等待第二個 printf(),原因就是 windows 下的輸出裝置沒有緩衝區,遇到 printf() 立即就輸出了,不會有延遲。所謂重新整理緩衝區,就是將緩衝區中的內容送達到目的地。緩衝區的重新整理遵循以下的規則:緩衝區位於使用者程式和硬體裝置之間,用來快取資料,
目的是讓快速的 cpu 不必等待慢速的輸入輸出裝置,同時減少操作硬體的次數。
對於 io 密集型的網路應用程式,比如**、資料庫、dns、cdn 等,緩衝區的設計至關重要,它能十倍甚至一百倍得提高程式效能。
緩衝區 快取
快取 cache 所以兩個的區別 緩衝區是記憶體空間的一部分 用來緩衝輸入和輸出的資料 緩衝區具有一部分大小 緩衝區根據對應的是輸入裝置還是輸出裝置分為輸入緩衝區,輸出緩衝區 buffer的核心作用是用來緩衝,緩和衝擊。比如你每秒要寫100次硬碟,對系統衝擊很大,浪費了大量時間在忙著處理開始寫和結束...
輸入緩衝區與輸出緩衝區
本博文通過一段程式來理解輸入緩衝區與輸出緩衝區。程式如下 author wanghao created time thu 17 may 2018 06 03 12 ampdt file name test.c description include int main int argc,const c...
緩衝區是什麼?
緩衝區,說白了,就是乙個中介。比如你要租乙個房子,剛好房子的主人也要出租房子,你們可以直接聯絡,但問題就在於 房子的主人可能沒有時間和你扯太多,你也沒有時間和房子的主人扯這扯那,這個時候,中介產生了。中介有時間和你扯皮,中介和房子的主人也提前溝通好。在上面的例子裡,你通過中介一次性把資訊反饋給房子主...