C語言知識彙總 29 進入緩衝區(快取)的世界

2021-10-24 12:00:08 字數 3263 閱讀 9315

緩衝區(buffer)又稱為快取(cache),是記憶體空間的一部分。也就是說,計算機在記憶體中預留了一定的儲存空間,用來暫時儲存輸入或輸出的資料,這部分預留的空間就叫做緩衝區(快取)。

有時候,從鍵盤輸入的內容,或者將要輸出到顯示器上的內容,會暫時進入緩衝區,待時機成熟,再一股腦將緩衝區中的所有內容「倒出」,我們才能看到變數的值被重新整理,或者螢幕產生變化。

有時候,使用者希望得到最及時的反饋,輸入輸出的內容就不能進入緩衝區。

緩衝區是為了讓低速的輸入輸出裝置和高速的使用者程式能夠協調工作,並降低輸入輸出裝置的讀寫次數。

使用者程式的執行速度可以看做 cpu 的執行速度,如果沒有各種硬體的阻礙,理論上它們是同步的。例如,我們都知道硬碟的速度要遠低於 cpu,它們之間有好幾個數量級的差距,當向硬碟寫入資料時,程式需要等待,不能做任何事情,就好像卡頓了一樣,使用者體驗非常差。計算機上絕大多數應用程式都需要和硬體打交道,例如讀寫硬碟、向顯示器輸出、從鍵盤輸入等,如果每個程式都等待硬體,那麼整台計算機也將變得卡頓。

但是有了緩衝區,就可以將資料先放入緩衝區中(記憶體的讀寫速度也遠高於硬碟),然後程式可以繼續往下執行,等所有的資料都準備好了,再將緩衝區中的所有資料一次性地寫入硬碟,這樣程式就減少了等待的次數,變得流暢起來。

緩衝區的另外乙個好處是可以減少硬體裝置的讀寫次數。其實我們的程式並不能直接讀寫硬體,它必須告訴作業系統,讓作業系統核心(kernel)去呼叫驅動程式,只有驅動程式才能真正的操作硬體。從使用者程式到硬體裝置要經過好幾層的轉換,每一層的轉換都有時間和空間的開銷,而且開銷不一定小;一旦使用者程式需要密集的輸入輸出操作,這種開銷將變得非常大,會成為制約程式效能的瓶頸。

這個時候,分配緩衝區就是必不可少的。每次呼叫讀寫函式,先將資料放入緩衝區,等資料都準備好了再進行真正的讀寫操作,這就大大減少了轉換的次數。實踐證明,合理的緩衝區設定能成倍提高程式效能。緩衝區其實就是一塊記憶體空間,它用在硬體裝置和使用者程式之間,用來快取資料,目的是讓快速的 cpu 不必等待慢速的輸入輸出裝置,同時減少操作硬體的次數。

根據不同的標準,緩衝區可以有不同的分類。

根據緩衝區對應的是輸入裝置還是輸出裝置,可以分為輸入緩衝區和輸出緩衝區。

根據資料重新整理(也可以稱為清空緩衝區,就是將緩衝區中的資料「倒出」)的時機,可以分為全緩衝、行緩衝、不帶緩衝。

在這種情況下,當緩衝區被填滿以後才進行真正的輸入輸出操作。緩衝區的大小都有限制的,比如 1kb、4mb 等,資料量達到最大值時就清空緩衝區。

在實際開發中,將資料寫入檔案後,開啟檔案並不能立即看到內容,只有清空緩衝區,或者關閉檔案,或者關閉程式後,才能在檔案中看到內容。這種現象,就是緩衝區在作怪。

在這種情況下,當在輸入或者輸出的過程中遇到換行符時,才執行真正的輸入輸出操作行緩衝的典型代表就是標準輸入裝置(也即鍵盤)和標準輸出裝置(也即顯示器)。

① 在講解 printf() 時,在 linux 或者 mac os 平台測試了如下的**:

#include #include int main()

printf("你好,世界\n");
② 對於 scanf(),不管使用者輸入多少內容,只要不按下回車鍵,就不進行真正的讀取。這是因為 scanf() 是帶有行緩衝的,使用者輸入的內容會先放入緩衝區,直到使用者按下回車鍵,產生換行符\n,才會重新整理緩衝區,進行真正的讀取。

不帶緩衝區,資料就沒有地方快取,必須立即進行輸入輸出。

getche()、getch() 就不帶緩衝區,輸入乙個字元後立即就執行了,根本不用按下回車鍵。

windows 下的 printf() 也不帶緩衝區,不管最後有沒有換行符\n,都會立即輸出,所以對於類似的輸出**,錯誤資訊輸出函式 perror() 也沒有緩衝區。錯誤資訊必須刻不容緩、立即、馬上顯示出來,緩衝區將會增加捕獲錯誤的時間,這是毫無理由的。

c語言標準規定,輸入輸出緩衝區要具有以下特徵:

現代計算機已經沒有了專門的錯誤顯示裝置,所有的資訊都顯示到乙個螢幕上,這裡的錯誤顯示裝置只能是計算機的顯示器。上面提到的 perror() 其實就是向錯誤顯示裝置上輸出資訊,但是現代計算機已經把顯示器作為了錯誤顯示裝置,所以 perror() 也是向顯示器上輸出內容。

所謂互動裝置,就是現代計算機上的顯示器和鍵盤。c標準雖然規定它們不能是全緩衝的,但並沒有規定它們到底是行緩衝還是不帶緩衝,這就導致不同的平台有不同的實現。

scanf()、getchar()、gets() 就是從輸入裝置(鍵盤)上讀取內容。對於輸入裝置,沒有緩衝區將導致非常奇怪的行為,比如,我們本來想輸入乙個整數 947,沒有緩衝區的話,輸入 9 就立即讀取了,根本沒有機會輸入 47,所以,沒有輸入緩衝區是不能接受的。windows、linux、mac os 在實現時都給輸入裝置帶上了行緩衝,所以 scanf()、getchar()、gets() 在每個平台下的表現都一致。

但是在某些特殊情況下,我們又希望程式能夠立即響應使用者按鍵,例如在遊戲中,使用者按下方向鍵人物要立即轉向,而且越快越好,這肯定就不能帶有緩衝區了。windows 下特有的 getche() 和 getch() 就是為這種特殊需求而設計的,它們都不帶緩衝區。

printf()、puts()、putchar() 就是向輸出裝置(顯示器)上顯示內容。對於輸出裝置,有沒有緩衝區其實影響沒有那麼大,頂多是晚一會看到內容,不會有功能性的障礙,所以 windows 和 linux、mac os 採用了不同的方案:

在講解 printf() 時,在 windows 平台測試了如下的**:

1 #include 2 #include 3 int main()

4

執行程式後,會發現第乙個 printf() 首先輸出(程式執行後立即輸出),等待 5 秒以後,第二個 printf() 才輸出,第乙個 printf() 不會等待第二個 printf(),原因就是 windows 下的輸出裝置沒有緩衝區,遇到 printf() 立即就輸出了,不會有延遲。

所謂重新整理緩衝區,就是將緩衝區中的內容送達到目的地。緩衝區的重新整理遵循以下的規則:

緩衝區位於使用者程式和硬體裝置之間,用來快取資料,目的是讓快速的 cpu 不必等待慢速的輸入輸出裝置,同時減少操作硬體的次數。對於 io 密集型的網路應用程式,比如**、資料庫、dns、cdn 等,緩衝區的設計至關重要,它能十倍甚至一百倍得提高程式效能。

C語言 清空緩衝區

考慮到下面的 includeint main while n 1 return 0 就是輸入乙個數,然後輸出這個數,如果輸入 1 就表示結束。但是如果使用者不小心輸入了乙個字母 或者其他非數字字元,就會發生死迴圈,這是為什麼呢?這是因為scanf函式是從緩衝區讀取乙個指定格式的變數。並且返回成功讀入...

C語言清除輸入緩衝區

在使用scanf輸入時,如果輸入不合法,此時可以檢查其返回值為0,但下次再呼叫時,上次不合法的輸入,還在輸入緩衝區中,此時就會接著不合法,如下 include int main int argc,char argv printf you input is d n id return 0 這樣,如果輸...

C語言清除輸入緩衝區

在使用scanf輸入時,如果輸入不合法,此時可以檢查其返回值為0,但下次再呼叫時,上次不合法的輸入,還在輸入緩衝區中,此時就會接著不合法,如下 include int main int argc,char argv printf you input is d n id return 0 這樣,如果輸...