Why Input functions cannot follow output functions or vice versa in C?

Viewed 151

My textbook says:

"An input function cannot follow an output function without an intervening call to fflush, fseek, fsetpos, or rewind and vice versa The first restriction on stream I/O can be worked around by adopting a discipline of flushing the buffer before every input operation. However, the only way to work around the second restriction is to open two streams on the same open socket descriptor, one for reading and one for writing."

So my questions are:

  1. Why can't input functions follow output functions or vice versa?
  2. Why can't the second restriction be fixed by adding a fflush operation?
1 Answers

The C standard says something a bit similar, and your book is presumably paraphrasing what it says:

C11 ¶7.21.5.3 The fopen function

¶7 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.

The rules allow the library to keep things under control. It might have to clean up characters pushed back by ungetc(), or arrange to flush output so that the input can be done sanely, or whatever. Note that these rules are for file streams (FILE *). Most often, when you're working with sockets, you use file descriptors (int), not file streams, and these rules are simply inapplicable.

Note that fseek(fp, 0, SEEK_CUR) is a positioning operation that leaves the input position where it is (seek zero bytes from the current offset), apart perhaps from losing characters pushed back by ungetc().

Using fflush() is a heavy-handed way of doing operations by comparison with using fseek().

With sockets, a single file descriptor is used for both reading from and writing to the remote party. But, because the file descriptor functions have no buffering, there is no confusion about what to do with data that was read but not yet consumed (as can easily happen with reading via a standard I/O stream), nor is there any confusion about what to do with data that was written to the I/O buffers but not yet sent to the remote. Also, sockets are not seekable devices, unlike disk files. Pipes, FIFOs, terminals, and many other non-disk, non-tape devices are also not seekable either. That doesn't relieve you of following the rules, but the updatable streams are normally intended for devices like disks where you'll be able to read back what you wrote, which is not true for the non-seekable devices.

So, if you really do need file streams for socket I/O, you probably do want to use two separate streams, one solely for reading and one solely for writing. On a POSIX system, you might use dup() or dup2() to create a copy of the socket file descriptor and then use fdopen() twice to create the read and write streams.

Related