1

It's easy to find popular conventions for C-style I/O. What's more difficult is finding explanations as to why they are such. It's common to see a read with statements like:

fread(buffer, sizeof(buffer), 1, ptr);

  • How should a programmer think about using the parameters size and n of fread()?

  • For example, if my input file is 100 bytes, should I opt for a larger size with fewer n or read more objects of a smaller size?

  • If the size-to-be-read and n exceed the byte-size of an input file, what happens? Are the excess bytes that were read composed, colloquially speaking, of "junk values"?

Tantillo
  • 367
  • 1
  • 5
  • 1
    "It's easy to find popular conventions for C-style I/O" - from your post, popular but wrong. See https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong and you should always examine the return value of read functions such as fread. –  Aug 24 '19 at 15:57
  • Absolutely, which explains the desire for an intuitive explanation. I updated my question to focus on fread(). – Tantillo Aug 24 '19 at 16:03
  • Which language: C or C++? – JaMiT Aug 24 '19 at 16:10
  • The syntax focuses on C-style I/O, but my thinking is that the intuition extends across both languages. If this is wrong, I can remove C++ from the tag list. – Tantillo Aug 24 '19 at 16:12
  • @πάνταῥεῖ Following the OP's edit, the dupe is not applicable. –  Aug 24 '19 at 16:16

2 Answers2

3
size_t fread(void * restrict ptr, size_t size, size_t n, FILE * restrict stream);

How should a programmer think about using the parameters size and n of fread()?

When reading into an array:

size is the size of called pointer's de-referenced type.

n in the number of elements.

some_type destination[max_number_of_elements];
size_t num_read = fread(destination, sizeof *destination, max_number_of_elements, inf);
printf("Number of elements read %zu\n", num_read);

if (num_read == 0) {
  if (feof(inf)) puts("Nothing read as at end-of-file");
  else if (ferror(inf)) puts("Input error occurred");
  else {
    // since sizeof *destination and max_number_of_elements cannot be 0 here
    // something strange has occurred (UB somewhere prior?)
  }

For example, if my input file is 100 bytes, should I opt for a larger size with fewer n or read more objects of a smaller size?

In the case, the size of the data is 1, the max count 100.

#define MAX_FILE_SIZE 100
uint8_t destination[MAX_FILE_SIZE];
size_t num_read = fread(destination, sizeof *destination, MAX_FILE_SIZE, inf);

If the size-to-be-read and n exceed the byte-size of an input file, what happens?

The destination is not filled. Use the return value to determine.

Are the excess bytes that were read composed, colloquially speaking, of "junk values"?

No. There values before fread() remain the same, (as long as the return was not 0 and ferror() not set). If the destination was not initialized/assigned, then yes, it may be though of as junk.


Separate size, n allows fread() to function as desired even if size * n overflows size_t math. With current flat memory models, rarely is this needed.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

First, the while (!feof(ptr)) is wrong and a really bad anti-pattern. There are situations where it can work, but it's almost always gratuitously more complicted that correct idiomatic usage. The return value of fread or other stdio read functions already tells you if it didn't succeed, and you usually need to be able to handle that immediately rather than waiting for the next loop iteration to start. If whatever resource you're learning from is teaching this while (!feof(ptr)) thing, you should probably stop trusting it as a source for learning C.

Now, on to your specific question about the size and n arguments: having them separate is completely gratuitous and not useful. Just pass the desired length to read for one of them, and 1 for the other. If you want to be able to determine how many bytes were already read if you hit end-of-file or an error, you need to pass 1 for size and the requested number of bytes as n. Otherwise, if any read shorter than expected is an error, it sometimes makes sense to switch them; then the only possible return values are 1 and 0 (success and error, respectively).

For an understanding of why it's the case that how you use these two arguments don't matter, all the stdio read functions, including fread, are specified as if they happened via repeated calls to fgetc. It does not matter if you have size*n such calls or n*size such calls, because multiplication of numbers commutes.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711