關於標準IO緩衝區的問題

關於標準IO緩衝區的問題

按照標準IO緩衝區可以分為三類:

不緩存類型:

  • 一旦有數據,直接將數據寫入到文件

行緩衝類型:

  • 同全緩衝類型
  • 遇到\n時,將數據寫入文件

全緩衝類型:

  • 當程序結束,將數據沖洗到文件
  • 當遇見fflush(),將數據沖洗到文件
  • 當文件關閉時,將數據沖洗到文件
  • 當遇到讀取操作,將數據沖洗到文件
  • 當改變緩衝區的類型時,將數據沖洗到文件
  • 當緩衝區滿了,將數據沖洗到文件

對於標準輸出而言,默認是行緩衝
對於標準出錯而言,默認是不緩存
對於普通文件而言,默認是全緩衝

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    // w+ 以讀寫的方式打開文件,若文件存在則清空文件數據,若文件不存在,則創建文件
    FILE *fp = fopen("_a.txt", "w+");

    // 將數據寫入文件
    fputs("123", fp);
    
    //暫停程序不讓,不讓程序正常結束,觀察a.txt文件內容
    pause();
    return 0;
}

我們能看到文件被創建,但是文件內沒有內容。
我們來分析一下,我們此時是以普通文件操作,此時是默認的全緩衝,且調用了pause()函數,程序沒有正常的結束,所以能猜想到此刻的內容是在緩衝區里,沒有被沖洗至文件中。如果我們把pause()進行注釋,在執行該程序,我們能看到我們要寫入的數據出現在_a.txt文件中。這也是全緩衝類型的第一次情況,當程序結束,將數據讀取到文件。

接下來我們來看一下這段代碼:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("_a.txt", "w+");

    fputs("123", fp);
    fflush(fp);

    pause();
    return 0;
}

印證第二種情況我們直接用沖洗函數fflush(),能看到數據成功讀取到文件中。

第三種情況,在fput函數和pause函數中間,調用fclose(),關閉文件。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("_a.txt", "w+");

    fputs("123", fp);
    fclose(fp);

    pause();
    return 0;
}

第四種情況,當遇到讀取操作,將數據沖洗到文件。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("_a.txt", "w+");

    fputs("123", fp);
    fgetc(fp);

    pause();
    return 0;
}

接下來我們看一下通過setvbuf()改變緩衝區的類型,進而對緩衝區的沖洗操作。

簡單的介紹一下:setvbuf函數

image
這圖片是我在man手冊截圖下來的。

int setvbuf(FILE *stream, char *buffer, int mode, size_t size)
stream — 這是指向 FILE 對象的指針,該 FILE 對象標識了一個打開的流。
buffer — 這是分配給用戶的緩衝。如果設置為 NULL,該函數會自動分配一個指定大小的緩衝。
mode — 這指定了文件緩衝的模式。

_IOFBF 全緩衝
_IOLBF 行緩衝
_IONBF 無緩衝

size –這是緩衝的大小,以位元組為單位。

返回值:如果成功,則該函數返回 0,否則返回非零值。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("_a.txt", "w+");
 // setvbuf(fp, NULL, _IONBF, 0);
    fputs("123", fp);

    setvbuf(fp, NULL, _IONBF, 0);

    pause();
    return 0;
}

我們這裡的語句是setvbuf(fp, NULL, _IONBF, 0),把普通文件默認的全緩衝,轉換成無緩衝,數據在文件中有所顯示,所以符合我們的第五條理論。這是為了突出我們的第五條理論,因此將該語句放置在fput語句的後面,當然如果我們在setvbuf(fp, NULL, _IONBF, 0)後面寫上相同的fputs語句或者將setvbuf(fp, NULL, _IONBF, 0),放置至fopen語句後面也是會相同的效果。不過第二種情況的理解不同,此刻能成功讀取數據不是因為緩衝區類型的轉換,而是因為它此刻已經被定義為無緩衝,所以數據直接讀取至文件中。

我們這裡不妨試一試自己定義一個100位元組的buf緩衝區,且在讀取數據之前就將緩衝區類型轉換至行緩存,觀察是否能通過\n來將數據讀取至文件當中去。(沖洗行緩衝)。


#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("_a.txt", "w+");
    char buf[1024];
    
    setvbuf(fp, buf, _IOLBF, 1024);

    //這裡是行緩衝,如果要將數據沖洗至普通文件,注意要加\n
    fputs("123\n", fp);
    
    pause();
    return 0;
}

總之,滯留在緩衝區中的數據有時被稱為臟數據(dirty data),臟數據的存在代表程序操作的結果與文件真實狀態不一致,若未正常沖洗這些臟數據就退出程序則有可能會造成數據丟失,我們在使用IO對數據操作時要注意對緩衝區的問題

Tags: