tags:

views:

1677

answers:

5

We had a discussion here at work regarding why fread and fwrite take a size per member and count and return the number of members read/written rather than just taking a buffer and size. The only use for it we could come up with is if you want to read/write an array of structs which aren't evenly divisible by the platform alignment and hence have been padded but that can't be so common as to warrant this choice in design.

From FREAD(3):

The function fread() reads nmemb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.

The function fwrite() writes nmemb elements of data, each size bytes long, to the stream pointed to by stream, obtaining them from the location given by ptr.

fread() and fwrite() return the number of items successfully read or written (i.e., not the number of characters). If an error occurs, or the end-of-file is reached, the return value is a short item count (or zero).

+10  A: 

The difference in fread(buf, 1000, 1, stream) and fread(buf, 1, 1000, stream) is, that in the first case you get only one chunk of 1000 bytes or nuthin, if the file is smaller and in the second case you get everything in the file less than and up to 1000 bytes.

Peter Miehle
Although true, that only tells a small part of the story. It would be better to contrast something reading, say, an array of int values, or an array of structures.
Jonathan Leffler
This would make a great answer if the justification was completed.
Matt Joiner
+5  A: 

It's based on how fread is implemented.

The Single UNIX Specification says

For each object, size calls shall be made to the fgetc() function and the results stored, in the order read, in an array of unsigned char exactly overlaying the object.

fgetc also has this note:

Since fgetc() operates on bytes, reading a character consisting of multiple bytes (or "a multi-byte character") may require multiple calls to fgetc().

Of course, this predates fancy variable-byte character encodings like UTF-8.

The SUS notes that this is actually taken from the ISO C documents.

R. Bemrose
+2  A: 

Likely it goes back to the way that file I/O was implemented. (back in the day) It might have been faster to write / read to files in blocks then to write everything at once.

dolch
Not really. The C specification for fwrite notes that it makes repeated calls to fputc: http://www.opengroup.org/onlinepubs/009695399/functions/fwrite.html
R. Bemrose
+3  A: 

Here, let me fix those functions:

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);
}

As for a rationale for the parameters to fread()/fwrite(), I've lost my copy of K&R long ago so I can only guess. I think that a likely answer is that Kernighan and Ritchie may have simply thought that performing binary I/O would be most naturally done on arrays of objects. Also, they may have thought that block I/O would be faster/easier to implement or whatever on some architectures.

Even though the C standard specifies that fread() and fwrite() be implemented in terms of 'fgetc() and fputc()`, remember that the standard came into existence long after C was defined by K&R and that things specified in the standard might not have been in the original designers ideas. It's even possible that things said in K&R's "The C Programming Language" might not be the same as when the language was first being designed.

Finally, here's what P.J. Plauger has to say about fread() in "The Standard C Library":

If the size (second) argument is greater than one, you cannot determine whether the function also read up to size - 1 additional characters beyond what it reports. As a rule, you are better off calling the function as fread(buf, 1, size * n, stream); instead of fread(buf, size, n, stream);

Bascially, he's saying that fread()'s interface is broken. For fwrite() he notes that, "Write errors are generally rare, so this is not a major shortcoming" - a statement I wouldn't agree with.

Michael Burr
Actually I often like doing it the other way: `fread(buf, size*n, 1, stream);` If incomplete reads are an error condition, it's simpler to arrange for `fread` to simply return 0 or 1 rather than the number of bytes read. Then you can do things like `if (!fread(...))` instead of having to compare the result against the requested number of bytes (which requires extra C code and extra machine code).
R..
+1  A: 

This is pure speculations, however back in the days(Some are still around) many filesystems were not simple byte streams on a hard drive.

Many file systems were record based, thus to satisfy such filesystems in an efficient manner, you'll have to specify the number of items ("records"), allowing fwrite/fread to operate on the storage as records, not just byte streams.

nos
I'm glad someone brought this up. I did a lot of work with filesystem specs and FTP and records/pages and other blocking concepts are very firmly supported, although nobody uses those parts of the specs anymore.
Matt Joiner