從標準輸入輸出看流和緩衝區

2021-08-19 13:47:01 字數 3661 閱讀 1598

學習標準輸入輸出,我們都會遇到乙個概念,流和緩衝區,但到底什麼是流,什麼是緩衝區呢?

書《c primer plus》上說,c程式處理乙個流而不是直接處理檔案。後面的解釋十分抽象:『流(stream)是乙個理想化的資料流,實際輸入或輸出對映到這個資料流』。這個流具體是乙個怎麼樣的東西呢?

流這個定義非常的形象。我們可以這樣理解:

你宣告乙個file *fp ,並把fopen(某個檔案)返回的值賦予fp這兩個動作就相當於建立了乙個水龍頭,當你用getc(fp)之類的輸入函式讀取檔案字元時就相當於擰開了水龍頭,每讀取乙個字元,這個檔案就像水一樣的流動一下,fp所指的位址自然就向後移動了一位。

int ch;  

while((ch=getc(fp))!=eof)

putchar(ch);

你看這個迴圈,可以讀取乙個檔案的所有字元。如果不是流的話,ch永遠是第乙個字元,不會更新。也可以理解為,fp自動++(乙個字元的大小)。

但流的概念意味著什麼呢?

各大權威對流的說法有些不一致,我認為流既是資料的源或目的地的抽象,也是源和目的地之間流動資訊的表示。但流起碼都暗含以下的幾個方面:

1. 流是乙個抽象的概念,是對資訊的一種表達;在程式中,流就是對某個物件輸入輸出資訊的抽象。就像運輸工具是對一切運動載體的抽象一樣。

2. 流是一種「動」的概念,靜止儲存在介質上的資訊只有當它按一定的序列準備「運動」時才稱為流。「從程式移進或移出位元組」就是「動」的表現。靜止的資訊具有流的潛力,但不一定是流,就像沒有汽油不能行走的汽車一樣,它具有運輸工具的潛力,但它還不是運輸工具(因為它很有可能被當作房子來用了,我就在大街上看見有精明的商人用火車車廂來做酒吧)。

3. 流有源頭也有目的地;程式中各種移動的資訊都有其源和目的,記得程式設計(特別是彙編)時,老是要確定好某個操作的源運算元和目的運算元。借用佛教一言也即是:「萬物皆有因果」,這也就像長江一樣,西自唐古拉,而東去太平洋。在高速公路上飛跑的汽車,它必有其出發地和目的地。

4. 流一定帶有某種資訊,沒有任何內容的流帶著自身來表達「空」資訊。就像運輸工具一樣,它不運貨的時候就運著自己這一身的零件(包括駕駛員)並把一樣東西運到目的地,那就是它自己和乙個「跑空車」的資訊。流有最小的資訊單元就是二進位制位,含有最小的資訊包就是位元組,c標準庫提供兩種型別的流:二進位製流(binary stream)和文字流(text stream)。二進位製流是有未經處理的位元組構成的序列;文字流是由文字行組成的序列。而在著名的unix系統中,文字流和二進位製流是相同的(identical)。

5. 流有源頭也有目的地,那麼它必定與源頭和目的地相關聯。但人們操作流的時候,最關心的還是其目的地,也就是乙個定向(orientation)的意思,就像司機運貨一樣,它首要關心的問題是目的地,而非起點(操作者都知道)。在c語言中,通過開啟流來關聯流及其目的地,使用的函式是fopen(),該函式返回乙個指向檔案的指標(file *),該指標包含了足夠的可以控制流準確地到達目的地的資訊。

file是乙個結構體(摘自tc2.0中stdio.h檔案)

/* definition of the control structure for streams 

*/typedef

struct file; /* this is the file object */

將它稱為流控制結構體(control structure for streams)真好表現出其功能來。舉個例子就好像一卡車司機要把貨物運到x公司,公司主管就會給他一張地圖及x公司的基本資訊,這些材料所提供的資訊如果足夠的話,那麼它就能指導著司機準確地將貨物送達了。c中file這個結構體所起的作用就好像是運輸公司把一切有用的指導資訊封裝起來的檔案袋一樣。而已有關聯的流要終止這種關聯,就必須關閉流,使用的函式是fclose(),就像運貨公司若不再給x公司運貨了,那麼他們就必須要終止合作協議了。

這裡要注意的是:c語言中stdin、stdout、stderr分別是標準輸入流、標準輸出流及標準出錯流的邏輯目的,他們都預設對應相應的物理終端。在程式執行伊始,不需要進行open()操作,流自動開啟。

那緩衝區又是什麼意思呢?

緩衝區(buffer):

為了匹配計算機快速裝置和慢速裝置間的通訊步伐,計算機中大量使用硬體緩衝區(如cpu中的cache,記憶體相對於硬碟和cpu),流是傳輸資訊的一種邏輯表示,對流的各種不同操作也可能存在使用緩衝的需求。但是這裡的buffer只是一種邏輯概念,不是物理裝置。緩衝區存在於流與具體的裝置終端或者儲存介質上的檔案之間。就好像運貨到乙個公司裡一樣,合同上的要求是運到x公司,但是實際上是真的把貨物運到x公司的總部大樓嗎?不是。應該是運到x公司的倉庫中。這裡的倉庫就有點像我們所說的緩衝區了。也可以這麼說,流運動到目的,先經過的是快取區。

以scanf() printf()為例:

緩衝型別。

標準庫提供緩衝是為了減少對read和write的呼叫。提供的緩衝有三種型別(整理自apue):

全緩衝。

在這種情況下,實際的i/o操作只有在緩衝區被填滿了之後才會進行。對駐留在磁碟上的檔案的操作一般是有標準i/o庫提供全緩衝。緩衝區一般是在第一次對流進行i/o操作時,由標準i/o函式呼叫malloc函式分配得到的。

術語flush描述了標準i/o緩衝的寫操作。緩衝區可以由標準i/o函式自動flush(例如緩衝區滿的時候);或者我們對流呼叫fflush函式。

行緩衝

在這種情況下,只有在輸入/輸出中遇到換行符的時候,才會執行實際的i/o操作。這允許我們一次寫乙個字元,但是只有在寫完一行之後才做i/o操作。一般的,涉及到終端的流–例如標註輸入(stdin)和標準輸出(stdout)–是行緩衝的。

無緩衝

標準i/o庫不快取字元。需要注意的是,標準庫不快取並不意味著作業系統或者裝置驅動不快取。

當然,我們常用的scanf() 與 printf() 屬於行緩衝,下面我們來看個例子,可以幫助我們理解緩衝區在標準輸入輸出中的作用:

int main()  

while(1);

我們看看輸出結果:

fs@ubuntu

:~/qiang/char1

$ gcc -o 1

1.c

fs@ubuntu

:~/qiang/char1

$ ./1

打出是個空的,為什麼呢?

我們上面提到標準輸入輸出是行緩衝,即一行滿了才會重新整理,那什麼是重新整理呢?重新整理就是將資料從緩衝區取出來,真正能重新整理,要滿足什麼條件呢?

1、滿重新整理,即一行滿了(1024個位元組)才會重新整理;

2、遇到\n會重新整理;

3、呼叫fflush()函式;

4、程式結束 fclose();

我們可以看到上面的程式,應為有while(1),程式一直沒有結束,沒有\n,沒有滿行,沒有fflush(),所以並不會輸出;

這樣理解的話,我們可以改動一下了,就寫乙個吧,加\n:

int main()  

執行結果如下:

fs@ubuntu

:~/qiang/char1

$ gcc -o 1

1.c

fs@ubuntu

:~/qiang/char1

$ ./1

helloworld

C語言標準輸入輸出緩衝區

緩衝區特徵 緩衝例項 file結構定義 參考資料 緩衝區又稱為快取,它是記憶體空間的一部分。也就是說,在記憶體空間中預留了一定的儲存空間,這些儲存空間用來緩衝輸入或輸出的資料,這部分預留的空間就叫做緩衝區。緩衝區根據其對應的是輸入裝置還是輸出裝置,分為輸入緩衝區和輸出緩衝區。為什麼要引入緩衝區 比如...

Redis 輸入輸出緩衝區

id 客戶端連線的唯一標識,這個id是隨著redis的連線自增的,重啟redis後會重置為0 addr 客戶端連線的ip和埠 fd socket的檔案描述符,與lsof命令結果中的fd是同乙個,如果fd 1代表當前客戶端不是外部客戶端,而是redis內部的偽裝客戶端。name 客戶端的名字,後面的c...

C語言中的輸入輸出流和緩衝區(重點)詳解

c語言中我們用到的最頻繁的輸入輸出方式就是scanf 與printf scanf 從標準輸入裝置 鍵盤 讀取資料,並將值存放在變數中。printf 將指定的文字 字串輸出到標準輸出裝置 螢幕 注意寬度輸出和精度 輸出控制。c語言借助了相應的緩衝區來進行輸入與輸出。如上的簡圖 ok,我們接下來進入正題...