2
votes

I can't understand why function fread() behaves differently in these 2 examples:

1)
I have a structure with a short and a char (size is 4 bytes including padding) and an array of three such structures.If I write each short and char of each structure separately with fwrite() and then read that file with fread() to a variable whose type is that structure, I will read 4 bytes at a time (there will be 9 bytes in the file) so you can see that one byte will be left in 3rd iteration (and one byte will be lost in each iteration).What happens is that there is no 3rd read because I'm left with one byte and fread has to read 4 bytes.

2)
A simpler example, if I write a 1 byte char to a file with fwrite() and then put the content of that file into a 4 byte int with fread(), the integer will get that data.

Why does this happen?Why does the data get read in one case but not in the other if EOF is reached?

Here is the first example:

int main()
{
    struct X { short int s; char c; } y, x[]=
    {{0x3132,'3'},{0x3435,'6'},{0x3738,'9'}};
    FILE *fp=fopen("FILE.DAT","wb+");
    if (fp)
    {
        for(int i=0;i<sizeof(x)/sizeof(x[i]);)
        {
            fwrite(&x[i].s,sizeof(x[i].s),1,fp);
            fwrite(&x[i].c,sizeof(x[i].c),1,fp);
            i++;
        }
        rewind(fp);
        for(int i=0;fread(&y,sizeof(y),1,fp);)
        printf("%d:%x %c\n",++i, y.s, y.c);
        fclose(fp);
    }
return 0;
}  

Second example:

int main()
{
    FILE *fp=fopen("FILE.DAT","wb+");
    char c = 'a';
    fwrite(&c, sizeof(c), 1, fp);
    rewind(fp);

    int num;
    fread(&num, sizeof(num), 1, fp);
    fclose(fp);

return 0;
}  
2
Could you edit to provide a minimal reproducible example? Runnable code is a bit more concrete than text describing it.Charles Duffy
@Acorn there isn't a file, a new one is written (wb+ is used because it can both write and read).user3711671
Which of your two examples is the code demonstrating - just the first, right? Do you have a question about the first example? Or is the question just why the second example, which I can't visualize and you haven't shown code for, is different?Useless
You ask fread to read 1 item whose size is 4. a single item of size 4 does not exist (and clearly fread can't return a value like 0.5). What would you expect to happen? fread is allowed to read a smaller number of items (as in your second example), but reading part of an item isn't reasonable.Hasturkun
Your second example doesn't check the return value of fread, or initialize the int before calling it and check the value after. Are you sure it reads part of one integer?Useless

2 Answers

3
votes

... when EOF is reached ...

EOF isn't "reached". Many <stdio.h> functions return EOF as a signal that something went wrong, giving no indication what that something is. If you want to know what went wrong after receiving the signal, test with feof() and/or ferror().

3
votes

Why does the data get read in one case but not in the other if EOF is reached?

"What happens is that there is no 3rd read because I'm left with one byte and fread has to read 4 bytes." is a questionable premise.

1st Code did read 3 times. There are with no bytes left to read.

In both codes, the last read was a partial read with a fread() return value of 0.@Useless
(The first code did not print the result of the 3rd read.)

With fread(), a return value of 0 does not mean "end-of-file" was immediately encountered - nothing read. Instead, 0 means an complete read did not occur due to :
* "end-of-file" or partial read.
* rare I/O error.


Why does this happen?

In the 2nd code, results may differ due to Indeterminate behavior

fread() ... If a partial element is read, its value is indeterminate1 C11dr §7.21.8.1 2

fread(&num, sizeof(num), 1, fp) result may or may not be as expected.

A more informative example

int main(void) {
  FILE *fp = fopen("FILE.DAT", "wb+");
  char c = 'a';
  printf("  %8X\n", c);
  fwrite(&c, sizeof(c), 1, fp);
  rewind(fp);

  unsigned num = rand();
  printf("  %8X\n", num);
  size_t len = fread(&num, sizeof(num), 1, fp);
  printf("%zu %8X\n", len, num);
  len = fread(&num, sizeof(num), 1, fp);
  printf("%zu\n", len);
  fclose(fp);

  return 0;
}

Output

        61  as expected
  5851F42D  as expected - some random value
0 5851F461  Indeterminate!  (in this case, looks like the LSByte was replaced.)
0           as expected

Moral of the story: assess the return value of fread() before relying on what was read into the buffer.


1indeterminate value
either an unspecified value or a trap representation