天天看點

Linux c學習--從标準輸入輸出看流和緩沖區

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

      書《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  {
        short           level;          /* fill/empty level of buffer */
        unsigned        flags;          /* File status flags    */
        char            fd;             /* File descriptor      */
        unsigned char   hold;           /* Ungetc char if no buffer */
        short           bsize;          /* Buffer size          */
        unsigned char   *buffer;        /* Data transfer buffer */
        unsigned char   *curp;          /* Current active pointer */
        unsigned        istemp;         /* Temporary file indicator */
        short           token;          /* Used for validity checking */
}       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()為例:                      •   緩沖區(流)負責在輸入/輸出裝置和程式之間建立聯系。 –輸入裝置->記憶體緩沖區(stdin)->程式 –程式->記憶體緩沖區(stdout)->輸出裝置 •   是一塊臨時的存儲區域,或在記憶體中,或在裝置的控制卡上     . 緩沖類型。

标準庫提供緩沖是為了減少對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() 屬于行緩沖,下面我們來看個例子,可以幫助我們了解緩沖區在标準輸入輸出中的作用:

<pre class="cpp" name="code">#include <stdio.h>

int main()
while(1);
{
	printf("hello world");
	while(1);
}
           

我們看看輸出結果:

[email protected]:~/qiang/char1$ gcc -o 1 1.c
[email protected]:~/qiang/char1$ ./1

           

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

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

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

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

3、調用fflush()函數;

4、程式結束 fclose();

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

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

#include <stdio.h>

int main()
{
	printf("helloworld\n");
	while(1);
}
           

執行結果如下:

[email protected]:~/qiang/char1$ gcc -o 1 1.c
[email protected]:~/qiang/char1$ ./1
helloworld

           

可以看到列印出來了,其他方法在這就不寫了,大家可有從這個簡單的例子中看到緩沖區與流的概念,上面寫得可能比較亂,晚上時會進行再整理;

繼續閱讀