fread和fwrite函數,C語言fread和fwrite函數詳解

2020-07-16 10:04:25
對檔案格式化讀寫函數 fprintf 與 fscanf 而言,儘管它可以從磁碟檔案中讀寫任何型別的檔案,即讀寫的檔案型別可以是文字檔案、二進位制檔案,也可以是其他形式的檔案。但是,對二進位制檔案的讀寫來說,考慮到檔案的讀寫效率等原因,還是建議盡量使用 fread 和 fwrite 函數進行讀寫操作。

fread 與 fwrite 函數的原型如下面的程式碼所示:

size_t fread(void *buf, size_t size, size_t count, FILE *fp);
size_t fwrite(const void * buf, size_t size, size_t count, FILE *fp);

在上面的 fread 和 fwrite 函數原型中:
  • 引數 size 是指單個元素的大小(其單位是位元組而不是位,例如,讀取一個 int 型資料就是 4 位元組);
  • 引數 count 指出要讀或寫的元素個數,這些元素在 buf 所指的記憶體空間中連續存放,共占“size*count”個位元組。

即 fread 函數從檔案 fp 中讀出“size*count”個位元組儲存到 buf 中,而 fwrite 把 buf 中的“size*count”個位元組寫到檔案 fp 中。最後,函數 fread 和 fwrite 的返回值為讀或寫的記錄數,成功時返回的記錄數等於 count 引數,出錯或讀到檔案末尾時返回的記錄數小於 count,也可能返回 0。

需要注意的是,儘管 fread 和 fwrite 函數可以對資料進行成塊讀寫,但並不是說一次想讀寫多少資料就能全部讀寫多少資料,畢竟快取有限,而且不同的作業系統的快取大小也可能不一樣。同時,許多程式設計師還認為函數的引數 (size、count) 與位置對齊有關,甚至認為語句“fwrite(ptr,1,1024,fp)”的執行效率會比“fwrite(ptr,1024,1,fp)”高。實際情況並非如此,如在 glibc-2.17 庫中對 fwrite 函數的實現如下:
_IO_size_t _IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
    _IO_size_t request = size * count;
    _IO_size_t written = 0;
    CHECK_FILE (fp, 0);
    if (request == 0)
        return 0;
    _IO_acquire_lock (fp);
    if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
        written = _IO_sputn (fp, (const char *0 buf, request);
    _IO_release_lock (fp);
    if (written == request)
        return count;
    else if (written == EOF)
        return 0;
    else
        return written / size;
}
從上面的 fwrite 函數原始碼實現中可以清楚地看到:

首先,在把引數 size 與 count 傳進函數之後,第一步就是通過語句“_IO_size_t request=size*count;”來計算“size*count”,所以這兩個引數與什麼位置對齊根本沒有半點關係。

其次,在函數返回時,如果整個寫入成功(“written==request”),就返回 count;如果遇到 EOF(“written==EOF”),就返回 0;否則返回“written/size”。由此可見,函數返回的是成功寫入的塊數,而不是位元組數(除非 size 為 1),這樣做有許多好處。例如,在寫入多個結構體時,返回值能告訴你成功寫入的結構體的個數。當然,這樣看來,前面的“fwrite(ptr,1,1024,fp)”與“fwrite(ptr,1024,1,fp)”語句還是有所差別的。但是,如果呼叫者只關心是否全部寫入成功,那麼就完全沒必要糾結於語句“fwrite(ptr,1,1024,fp)”與“fwrite(ptr,1024,1,fp)”之間的差別了。

對於 fread 函數,其道理與 fwrite 函數完全一樣,如下面的函數原始碼所示:
_IO_size_t _IO_fread (void *buf,_IO_size_t size,_IO_size_t count,_IO_FILE *fp)
{
    _IO_size_t bytes_requested = size * count;
    _IO_size_t bytes_read;
    CHECK_FILE (fp, 0);
    if (bytes_requested == 0)
        return 0;
    _IO_acquire_lock (fp);
    bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
    _IO_release_lock (fp);
    return bytes_requested == bytes_read ? count : bytes_read / size;
}
除此之外,函數 fwrite 還與檔案的開啟模式有關。例如,如果檔案的開啟模式是“w+”,則是從檔案指標指向的地址開始寫,替換掉之後的內容,檔案的長度可以不變,fp 的位置移動 count 個數;如果檔案的開啟模式為“a+”,則從檔案的末尾開始新增,檔案的長度會不斷增加。