1. 程式人生 > 其它 >學習筆記第九章

學習筆記第九章

第九章

一、I/O庫函式與系統呼叫

(一)open()和fopen()相同點和不同點

第1行:在系統呼叫程式中,檔案描述符fd是一個整數;在庫I/O程式中,fp是一個檔案流指標。
第2行:系統呼叫open()開啟一個檔案進行讀取,並返回一個整數檔案描述符fd,弱國open()失敗,則返回-1.I/O庫函式fopen()返回一個FILE結構體指標,如果fopen()失敗,則返回NULL。
第3行:系統呼叫程式使用while迴圈讀取/寫入檔案內容。在每個迭代中,它發出read()系統呼叫,將最多4KB的字元讀入buf[]。然後,它將個字元從buf[]寫道檔案描述符l中,這是該程序的標準輸出。使用系統呼叫一次寫入一個位元組非常低效。相反,I/O庫程式僅僅使用fgetc(fp)從檔案流中獲取字元,通過putchar()輸出字元,直至檔案結束。

這兩個程式都會將src檔案複製到dest檔案中。由於第6章已經解釋過系統呼叫程式,所以我們只討論使用1/O庫函式的程式。

(1) fopen()使用字串表示模式,其中“r”表示READ,“w”表示 WRITE。它返回一個指向FILE結構體的指標。fopen()首先發出open()系統呼叫來開啟檔案,以獲取檔案描述符編號f。如果 open()系統呼叫失敗,則 fopen()會返回一個NULL指標。否則,它會在程式的堆區中分配一個FILE結構體。每個FILE結構體均包含一個內部緩衝區 fbuf[BLKSIZE],其大小通常與檔案系統的BLKSIZE相匹配。此外,它還包含用於操作 fbuf]的指標、計數器和狀態變數,儲存來自open()的檔案描述符。它將FILE結構體初始化並返回指向FILE結構體的f。需要注意的是,FILE結構體位於程序的使用者模式映像中。這意味著對I/O庫函式的呼叫是普通的函式呼叫,而不是系統呼叫。
(2)如有任何fopen()呼叫失敗,程式將會終止。如前文所述,fopen()在失敗時會返回一個NULL指標,例如,檔案不能在指定模式下開啟時。
(3)然後,它使用一個while迴圈來複制檔案內容。while迴圈的每個迭代嘗試從原始檔讀取BLKSIZE 位元組,並向目標檔案寫人n個位元組,其中n是從fread()返回的值。fread()'和 fwrite()的一般形式是:

int n=fread(buffer,size,nitems,FILEpet)
int n=fwrite(buffer,size,nitems,FILEpet)

系統函式:

  • open(),用於建立一個新的檔案描述符;
  • read(),讀取檔案,從檔案描述符 fildes 相關聯的檔案裡讀入 nbytes 個位元組的資料,並把它們放到資料區 buffer 中;
  • write(),把緩衝區 buffer 的前 nbytes 個位元組寫入與檔案描述符 fildes 關聯的檔案中;
  • lseek(),用於改變讀寫操作時的位置指標;
  • close(),終止檔案描述符 fildes 與其對應檔案之間的關聯。

I/O庫函式:

  • fopen(),主要用於對檔案和終端的輸入輸出;
  • fread(),主要作用是從一個檔案流裡讀取資料,資料從stream讀到由ptr指定的資料緩衝區裡面; fwirte(),從stream獲取資料記錄寫到ptr中,返回值是成功寫入的記錄個數;
  • fseek(),在檔案流裡面為下一次讀寫指定位置;
  • fclose(),關閉指定的文流stream,使所有未寫出的內容全部寫出。
    可以看出來,二者之間的函式功能基本可以一一對應,但是在系統呼叫中,使用了整數型變數檔案描述符fd,而在I/O庫函式中則使用了檔案流指標fp;系統呼叫的buffer最多包含4KB的字元,並且一般使用while迴圈一次一個位元組的寫入資料,效率十分低下。而I/O庫函式可以使用fgetc(fp)將檔案流中的所有資料讀取,十分高效。

二、I/O庫函式的演算法

(一)fread()

(1)在第一次呼叫fread()時,FILE結構體的緩衝區是空的,fread()使用儲存的檔案描述符fd發出一個
n=read(fa, fbuffer,BLKSIZE);
系統呼叫,用資料塊填充內部的fbuf[]。然後,它會初始化 fbuf[]的指標、計數器和狀態變數,以表明內部緩衝區中有一個數據塊。接著,通過將資料複製到程式的緩衝區,嘗試滿足來自內部緩衝區的fread()呼叫。如果內部緩衝區沒有足夠的資料,則會再發出一個read()系統呼叫來填充內部緩衝區,將資料從內部緩衝區傳輸到程式緩衝區,直到滿足所需的位元組數(或者檔案無更多資料)。將資料複製到程式的緩衝區之後,它會更新內部緩衝區的指標、計數器等,為下一個fread()請求做好準備。然後,它會返回實際讀取的資料物件數量。
(2)在隨後的每次fread()呼叫中,它都嘗試滿足來自FILE結構體內部緩衝區的呼叫。當緩衝區變為空時,它就會發出read()系統呼叫來重新填充內部緩衝區。因此,fread()一方面接受來自使用者程式的呼叫,另一方面向作業系統核心發出read()系統呼叫。

(二)fwrit()

fwrite()演算法與fread()演算法相似,只是資料傳輸方向不同。最開始,FILE結構體的內部緩衝區是空的。在每次呼叫fwrite()時,它將資料寫入內部緩衝區,並調整緩衝區的指標、計數器和狀態變數,以跟蹤緩衝區中的位元組數。如果緩衝區已滿,則發出write()系統呼叫,將整個緩衝區寫入作業系統核心。

(三)fclose演算法

若檔案以寫的方式被開啟,fclose()會先關閉檔案流的區域性緩衝區。然後,它會發出一個close(fd)系統呼叫來關閉FILE結構體中的檔案描述符。最後,它會釋放FILE結構體,並將FILE指標重置為NULL。

三、I/O庫函式模式

"r+":表示讀/寫,不會截斷檔案。
"w+":表示讀/寫,但是會先截斷檔案;如果檔案不存在,會建立檔案。
"a+":表示通過追加進行讀/寫;如果檔案不存在,會建立檔案。

四、限制混合fread-fwrite

fread()fwrite()會發出read()/write()系統呼叫來填充/清除內部緩衝區。當read()/write()使用檔案OFTE 中的讀/寫指標時,fread()/fwrite()會使用FILE結構體中區域性緩衝區的讀/寫指標。
如果沒有fseek()來同步這兩個指標,其結果就取決於它們在實現中的使用方式。為了避免出現任何不一致,我們將下面一行:
fseek (fp,(long) 20,0 ) ;
插入fread()和 fwrite()中間,結果會相同(而且正確)。

五、檔案流緩衝

無緩衝:從非緩衝流中寫入或讀取的字元將盡快單獨傳輸到檔案或從檔案中傳輸。例
如,檔案流stderr通常無緩衝。到stderr的所有輸出都會立即發出。
行緩衝:遇到換行符時,寫人行緩衝流的字元以塊的形式傳輸。例如,檔案流stdout
通常是行緩衝,逐行輸出資料。
全緩衝:寫人全緩衝流或從中讀取的字元以塊大小傳輸到檔案或從檔案傳輸。這是文
件流的正常緩衝方案。

最有收穫的內容

本章中最有收穫的內容是I/O庫函式的演算法,從底層瞭解了三個函式的詳細呼叫方法。

問題與解決思路

問題一:課本中多次提到了“巨集”,不是很理解他的具體意義。
解決方法:通過百度後瞭解到:預處理命令可以改變程式設計環境,提高程式設計效率,它們並不是 C 語言本身的組成部分,不能直接對 它們進行編譯,必須在對程式進行編譯之前,先對程式中這些特殊的命令進行“預處理” 。經過預處理後,程式就不再包括預處理命令了,最後再由編譯程式對預處理之後的源程式進行編譯處理,得到可供執行的目的碼。C 語言提供的預處理功能有三種,分別為巨集定義、檔案包含和條件編譯。
連結:https://blog.csdn.net/imgosty/article/details/81901183