ここで、freadとfwriteがメンバーごとにサイズを取得し、単にバッファーとサイズを取得するのではなく、読み取り/書き込みされたメンバーの数をカウントして返す理由について議論しました。使用できる唯一の用途は、プラットフォームの配置によって均等に分割できない構造体の配列を読み取り/書き込みするため、パディングされていますが、この選択を保証するほど一般的ではない場合ですデザインで。
FREAD(3)から:
関数fread()は、streamが指すストリームからデータのnmemb要素を読み込み、各サイズはバイト長で、ptrで指定された場所に格納します。
関数fwrite()は、各サイズがバイトの長さのnmembデータをstreamが指すストリームに書き込み、ptrで指定された場所からそれらを取得します。
fread()およびfwrite()は、正常に読み書きされた項目の数を返します(つまり、文字数ではありません)。エラーが発生した場合、またはファイルの終わりに達した場合、戻り値は短いアイテム数(またはゼロ)です。
fread の実装方法に基づいています。
Single UNIX Specificationによると
オブジェクトごとに、fgetc()関数に対してサイズの呼び出しを行い、結果を読み取り順に、オブジェクトを正確にオーバーレイするunsigned charの配列に格納します。
fgetc には次の注記もあります。
Fgetc()はバイトで動作するため、複数バイトで構成される文字(または「マルチバイト文字」)を読み取るには、fgetc()を複数回呼び出す必要があります。
もちろん、これはUTF-8のような派手な可変バイト文字エンコーディングよりも前のものです。
SUSは、これが実際にISO C文書から取られていることを示しています。
Fread(buf、1000、1、stream)とfread(buf、1、1000、stream)の違いは、最初の場合、ファイルが小さく、 2番目のケースでは、ファイル内のすべてを1000バイト以下で取得します。
これは純粋な憶測ですが、昔(一部はまだ残っています)に、多くのファイルシステムはハードドライブ上の単純なバイトストリームではありませんでした。
多くのファイルシステムはレコードベースであるため、そのようなファイルシステムを効率的に満たすには、アイテム(「レコード」)の数を指定する必要があります。
ここで、これらの機能を修正します。
_size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
return fread( ptr, 1, size, stream);
}
size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
return fwrite( ptr, 1, size, stream);
}
_
fread()
/fwrite()
のパラメーターの理論的根拠については、かなり前にK&Rのコピーを失ってしまったため、推測しかできません。おそらく、答えは、カーニハンとリッチーがバイナリI/Oの実行がオブジェクトの配列で最も自然に行われると単純に考えたかもしれないということだと思います。また、彼らはブロックI/Oがより速く/より簡単に実装されるか、またはいくつかのアーキテクチャで何でもするだろうと考えたかもしれません。
C標準では、fread()
とfwrite()
をfgetc()
とfputc()
の観点から実装するように指定されていますが、この標準が存在することに注意してくださいCがK&Rによって定義されてからかなり経ち、標準で指定されたものは元のデザイナーのアイデアにはなかったかもしれません。 K&Rの「Cプログラミング言語」で述べられていることは、言語が最初に設計されたときと同じではない可能性さえあります。
最後に、「標準Cライブラリ」のfread()
についてP.J. Plaugerが言わなければならないことがあります。
size
(2番目の)引数が1より大きい場合、関数がレポートする範囲を超えて_size - 1
_までの追加文字も読み取るかどうかを判断できません。原則として、fread(buf, 1, size * n, stream);
の代わりにfread(buf, size, n, stream);
として関数を呼び出す方が良いでしょう。
基本的に、彼はfread()
のインターフェースが壊れていると言っています。 fwrite()
について、「書き込みエラーは一般にまれなので、これは大きな欠点ではありません」-私は同意しない声明です。
ファイルI/Oが実装された方法に戻る可能性があります。 (当時)一度にすべてを書き込むよりも、ブロック単位でファイルを読み書きする方が高速だったかもしれません。
サイズとカウントに別々の引数を使用すると、部分的なレコードの読み取りを回避できる実装で有利になる可能性があります。パイプのようなものからシングルバイト読み取りを使用する場合、たとえ固定形式のデータを使用していても、レコードが2つの読み取りに分割される可能性を考慮しなければなりません。代わりにリクエストできる場合293バイトが使用可能な場合、それぞれ10バイトの最大40レコードの非ブロッキング読み取りで、システムが290バイト(29レコード全体)を返し、3バイトを次の読み取りに備えておくと、はるかに便利です。
Freadの実装がそのようなセマンティクスをどの程度処理できるかはわかりませんが、それらをサポートすることを約束できる実装では確かに便利です。
Cには関数のオーバーロードがないためだと思います。ある程度あれば、サイズは冗長になります。しかし、Cでは、配列要素のサイズを決定することはできません。1つ指定する必要があります。
このことを考慮:
int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);
Fwriteがバイト数を受け入れた場合、次のように記述できます。
int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);
しかし、それは単に非効率的です。 sizeof(int)倍のシステムコールがあります。
考慮すべきもう1つの点は、通常、配列要素の一部をファイルに書きたくないことです。整数全体が必要です。 fwriteは、正常に書き込まれた要素の数を返します。要素の下位2バイトのみが書き込まれていることがわかった場合、どうしますか?
一部のシステム(アライメントのため)では、コピーを作成してシフトしないと整数の1バイトにアクセスできません。