サイズとカウントを引数として使用するfread / fwriteの論理的根拠は何ですか?
質問
ここで作業中に、freadとfwriteがメンバーごとにサイズを取得し、単にバッファーとサイズを取得するのではなく、読み取り/書き込みされたメンバーの数をカウントして返すことについて議論しました。使用できる唯一の用途は、プラットフォームの配置によって均等に分割できない構造体の配列を読み書きする場合です。したがって、パディングされていますが、この選択を保証するほど一般的ではありません設計中。
FREAD(3)から:
関数fread()は、各サイズがバイト長のデータのnmemb要素を読み取ります。 streamが指すストリームから、指定された場所に保存する ptrによって。
fwrite()関数は、各サイズバイトのnmemb要素のデータを書き込みます long、streamが指すストリームに、場所から取得する ptrによって与えられます。
fread()およびfwrite()は、正常に読み書きされたアイテムの数を返します (つまり、文字数ではありません)。エラーが発生した場合、または ファイルの終わりに達した場合、戻り値は短いアイテム数(またはゼロ)です。
解決
fread の実装方法に基づいています。
Single UNIX Specificationによると
オブジェクトごとに、サイズの呼び出しは fgetc()関数と 結果は、読み取られた順序で、 符号なしcharの配列 オブジェクトのオーバーレイ。
fgetc にも次の注意事項があります。
fgetc()はバイトを操作するため、 からなる文字を読む 複数バイト(または"マルチバイト 文字")は複数の呼び出しが必要な場合があります fgetc()へ。
もちろん、これはUTF-8のような派手な可変バイト文字エンコーディングより前のものです。
SUSは、これが実際にISO C文書から取られていることに注意しています。
他のヒント
fread(buf、1000、1、stream)とfread(buf、1、1000、stream)の違いは、最初の場合、ファイルが小さい場合、1000バイトまたはnuthinのチャンクを1つだけ取得することです。 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によってCが定義されてからずっと後に標準が存在し、標準で指定されたものは元のデザイナーのアイデアにはなかったかもしれないことを思い出してください。 K& Rの「The C Programming Language」で述べられていることも可能です。言語が最初に設計されたときとは異なる場合があります。
最後に、P.J。Plaugerが" The Standard C Library"の fread()
について次のように言っています:
size
(2番目の)引数が1より大きい場合、判断できません 関数が報告する範囲を超えてsize-1
までの追加文字も読み取るかどうか。 原則として、fread(buf、1、size * n、stream);
として関数を呼び出す方が良いでしょうfread(buf、size、n、stream);
基本的に、彼は fread()
のインターフェースが壊れていると言っています。 fwrite()
の場合、「書き込みエラーは一般にまれなので、これは大きな欠点ではありません」と彼は述べています。 -同意しない声明。
同様に、ファイルI / Oが実装された方法に戻ります。 (当時)一度にすべてを書き込むよりも、ブロック単位でファイルを読み書きする方が高速だったかもしれません。
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バイトにアクセスできません。
サイズとカウントに別々の引数を持たせると、部分的なレコードの読み取りを回避できる実装で有利になる可能性があります。パイプのようなものからシングルバイト読み取りを使用する場合、たとえ固定形式データを使用していても、レコードが2つの読み取りに分割される可能性を考慮しなければなりません。代わりにリクエストできる場合293バイトが使用可能な場合、それぞれ10バイトの最大40レコードの非ブロッキング読み取りで、システムが290バイト(29レコード全体)を返す一方で、3バイトを次の読み取りに備えておくと、さらに便利になります。
このようなセマンティクスをfreadの実装がどの程度処理できるかはわかりませんが、それらをサポートすることを約束できる実装では確かに便利です。