fflush Fails to Switch From Read to Write on Files fopen’ed with ‘r+’ Access

The fopen documentation states:

When the "r+", "w+", or "a+" access type is specified, both reading and writing are allowed (the file is said to be open for "update"). However, when you switch between reading and writing, there must be an intervening fflush, fsetpos, fseek, or rewind operation.

Indeed? A brief experiment, if you please.

This indeed succeeds:

char buff[10]; 
FILE* fp = fopen("c:\\test.txt" ,"r+" ); 
// file contains, say, "1 \n 2 \n 3 \n 4 \n 5 \n" 
fgets(buff, 10, fp); // buff contains "1" 
fgets(buff, 10, fp); // buff contains "2" 
fseek(fp, 0, SEEK_CUR); 
fputs("a" , fp); // succeeds
fclose(fp); 

While this fails miserably:

char buff[10]; 
FILE* fp = fopen("c:\\test.txt","r+" ); 
fgets(buff, 10, fp); 
fgets(buff, 10, fp); 
fflush(fp); 
fputs("a" , fp); // fails 
fclose(fp); 

Stepping into the CRT source gives some conjectured reasons:

(1) fopen with ‘r+’ sets fp->_flag to (_IORW & ~(_IOREAD | _IOWRT)).

(2) fgets adds _IOREAD.

(3) fputs contains code (in _flswbuf), logically equivalent to –

    if (stream->_flag & _IOREAD && !(stream->_flag & _IOEOF))) 
    { 
     stream->_flag |= _IOERR; 
     return (_TEOF); 
    }

with an explicit comment (snip):

/* Note that _IOREAD and IOEOF both being set implies switching 
from read to write at end-of-file, which is allowed by ANSI. */

So indeed an explicit transition is mandated.

(4) fseek contains just such a transition. Code in _fseek_nolock reads:

 /* If file opened for read/write, clear flags since we 
     don't know what the user is going to do next [..snip] */
     if (stream->_flag & _IORW) 
     stream->_flag &= ~(_IOWRT|_IOREAD); 

(5) However, fflush does NOT. It does contain code (in _flush) equivalent to –

if ((stream->_flag & (_IOREAD | _IOWRT)) == _IOWRT [..snip]) 
{ 
     _write([..snip]); 
     if ( stream->_flag & _IORW ) 
     stream->_flag &= ~_IOWRT; 
}

So fflush allows transitioning ONLY from write to read.

This is either a CRT bug (my guess), or a documentation issue. Might it be a subtle ANSI-C conformance thingy (as hinted by the _flswbuf comment) that I’m missing? (it’s still a documentation issue in that case). Opinions are very welcome.

This is almost verbatim a feedback I submitted at MS connect a while ago following a forum advice. The issue was triaged (I imagine) and escalated to the product team, and after a few weeks its status changed to ‘fixed’ without any word of explanation.    Oh well.   Hopefully this post will help someone out there some day.

This entry was posted in VC++. Bookmark the permalink.

2 Responses to fflush Fails to Switch From Read to Write on Files fopen’ed with ‘r+’ Access

  1. hanoh says:

    It might be a tradition kept from the days a file was saved on a magnetic tape. This behavior is classic to computers in the 80’s. Also note that the robust way to work with file handles is to keep a handle per file, per operation (hence no READ nad WRITE operation can be done the same time on the same file). This behavior has been changed when DBMS were introduced in the 90’s where very big files kept a lot of data, needed to be accessed by processes at the same time and so on. Even then either you create a new read handle (SELECT for example), or a new write handle that will be changed later from write to read (UPDATE,INSERT for example). So traditionally the need to have a handle that changes from read to write never have existed.

Leave a reply to hanoh Cancel reply