7

I have a small example program written in C. I have a main that call a function writeFile that writes some numbers in a binary file. Then I call overwrite to replace 0 with 1 and finally I print the result.

This is the code:

#include <stdio.h>

/* Print the content of the file */
void printFile(){
    printf("Read test.dat:\n");
    int r;
    FILE* fp = fopen("test.dat", "rb+");
    if(fp) {
    while(fread(&r,sizeof(int),1,fp)){
            printf("%d\n", r);
        }
    }
    fclose(fp);
}

/* Replace 0 with 1 */
void overwrite(){
    int   r;
    FILE *fp = fopen("test.dat", "rb+");
    if (fp) {
        int i=0;
        while (i < 4 && fread(&r, sizeof(int), 1, fp)) {
            i++;
            if (r == 0) {
                r = 1;
                fseek(fp,-sizeof(int),SEEK_CUR);
                fwrite(&r,sizeof(int),1,fp);
            }
        }
    }
    fclose(fp);    
}

/* Create original file */
void writeFile() {
    int b, b1, b2, b3, b4;

    b  = 3;
    b1 = 2;
    b2 = 0;
    b3 = 4;

    FILE *fp = fopen("test.dat", "wb");
    if (fp) {
        fwrite(&b, sizeof(int), 1, fp);
        fwrite(&b1, sizeof(int), 1, fp);
        fwrite(&b2, sizeof(int), 1, fp);
        fwrite(&b3, sizeof(int), 1, fp);
    }
    fclose(fp);    
}

int main() {
    writeFile();
    printf("---------BEFORE--------\n");
    printFile();
    printf("-----------------------\n");
    printf("Overwriting...\n");
    overwrite();
    printf("---------AFTER---------\n");
    printFile();
    return 0;
}

This code works with Linux, but when I run the same code on Windows the output is this:

 ---------BEFORE--------
Read test.dat:
3
2
0
4
-----------------------
Overwriting...
---------AFTER---------
Read test.dat:
3
2
1
2

Not only 0 was replaced by 1 but also the last number changed. Someone can help me to understand why this happens?

Another problem is that in the overwrite I must use i to stop the while because without the i<4 I get an infinite loop (only with Windows).

I tested this code on Windows 8.1 compiled with gcc 4.8.1 (from MinGW). On my Linux machine I tested the code with gcc 5.1.1.

Thank you all,

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
RobotMan
  • 488
  • 4
  • 15

2 Answers2

7

It's because you need to fflush() after the fwrite(), since you should not call fread() after calling fwrite() without an intervening call to fflush(),

This is the section of the standard that is relevant in this case

7.21.5.3 The fopen function

  1. When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file. Opening (or creating) a text file with update mode may instead open (or create) a binary stream in some implementations.

It's weird that this section is within fopen() function since it involves fread() and fwrite(), which is where I was looking for an answer.

You can also see that my previous answer worked but not for the reason I stated in it, instead the explanation is found in the paragraph above.

Community
  • 1
  • 1
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 2
    The `fclose(fp)` should be in the `fp != NULL` branch (this also goes for the original code in the question). – Jens Jun 29 '15 at 13:30
  • 1
    My previous answer was wrong I am sorry, it didn't make much sense I must confess. – Iharob Al Asimi Jun 29 '15 at 13:38
  • Thank you, now works!! Before your fix, I tried to put some ftell(fp) to see the pointer position and everything seemed ok. The pointer increase after each read and when I replace the value it return back (after the fseek) and then return forward (after the fwrite). Why does the pointer seem at the right position? – RobotMan Jun 29 '15 at 13:55
  • That is the correct behavior, the problem is unrelated to that as you can see from my final answer. It really disturbed me not to find an answer. – Iharob Al Asimi Jun 29 '15 at 13:56
2

MSDN states for Stream I/O:

Input can follow output directly only with an intervening call to fflush or to a file-positioning function (fseek, fsetpos, or rewind). Output can follow input without an intervening call to a file-positioning function if the input operation encounters the end of the file.

So you'd have to call fflush() after calling fwrite:

void overwrite()
{
    FILE *fp = fopen("test.dat", "rb+");
    if (fp) {
        int   r;
        while (fread(&r, sizeof(int), 1, fp) == 1) {
            if (r == 0) {
                r = 1;
                fseek(fp,-sizeof(int),SEEK_CUR);
                fwrite(&r,sizeof(int),1,fp);
                fflush(fp); /* Flush here */
            }
        }
        fclose(fp); /* Also: call fclose() only when fopen() succeeded */
    }

}
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
lx.
  • 2,317
  • 1
  • 22
  • 32
  • Well, I did quote the text and it states: "Input can follow output directly only with an intervening call to fflush or [...]". I don't see what's wrong with that. – lx. Jun 29 '15 at 13:57
  • Oh - I didn't notice that you were also editting my post. But I still don't see what's wrong with my answer :| – lx. Jun 29 '15 at 13:58
  • I see, it's just in the opossite order as to the standard document it says **only** with ... whereas the standard says **without an intervening call ...**... So sorry for that, just try and understand that although I know I am good with the english language, it's not my native language. – Iharob Al Asimi Jun 29 '15 at 13:59
  • Alright - no worries and thanks for the nicer formatting :) – lx. Jun 29 '15 at 14:02
  • Just one thing, the MinGW has nothing to do with MSDN. But since it conforms to the standard just like gcc does, then your answer is still correct. – Iharob Al Asimi Jun 29 '15 at 14:02
  • 1
    MinGW links against MSVCRT: http://www.mingw.org/wiki/c99 , so MSDN should also apply in this case. Citing the C-standard is definitely the better source though. – lx. Jun 29 '15 at 14:06