Opening a Stream
The following three functions open a standard I/O stream.
FILE *fopen(const char *restrict pathname, const
char *restrict type);
FILE *freopen(const char *restrict pathname, const
char *restrict type,
FILE *restrict fp);
FILE *fdopen(int filedes, const char *type);
All three return: file pointer if OK, NULL on error
The differences in these three functions are as follows.
The fopen function opens a specified file.
The freopen function opens a specified file on a specified stream, closing the stream first if it is already open. If the stream previously had an orientation, freopen clears it. This function is typically used to open a specified file as one of the predefined streams: standard input, standard output, or standard error.
The fdopen function takes an existing file descriptor, which we could obtain from the open, dup, dup2, fcntl, pipe, socket, socketpair, or accept functions, and associates a standard I/O stream with the descriptor. This function is often used with descriptors that are returned by the functions that create pipes and network communication channels. Because these special types of files cannot be opened with the standard I/O fopen function, we have to call the device-specific function to obtain a file descriptor, and then associate this descriptor with a standard I/O stream using fdopen.
Both fopen and freopen are part of ISO C; fdopen is part of POSIX.1, since ISO C doesn't deal with file descriptors.
ISO C specifies 15 values for the type argument, shown in Figure.
Figure. The type argument for opening a standard I/O stream
r or rb
open for reading
w or wb
truncate to 0 length or create for writing
a or ab
append; open for writing at end of file, or create for writing
r+ or r+b or rb+
open for reading and writing
w+ or w+b or wb+
truncate to 0 length or create for reading and writing
a+ or a+b or ab+
open or create for reading and writing at end of file
Using the character b as part of the type allows the standard I/O system to differentiate between a text file and a binary file. Since the UNIX kernel doesn't differentiate between these types of files, specifying the character b as part of the type has no effect.
With fdopen, the meanings of the type argument differ slightly. The descriptor has already been opened, so opening for write does not truncate the file. (If the descriptor was created by the open function, for example, and the file already existed, the O_TRUNC flag would control whether or not the file was truncated. The fdopen function cannot simply truncate any file it opens for writing.) Also, the standard I/O append mode cannot create the file (since the file has to exist if a descriptor refers to it).
When a file is opened with a type of append, each write will take place at the then current end of file. If multiple processes open the same file with the standard I/O append mode, the data from each process will be correctly written to the file.
Versions of fopen from Berkeley before 4.4BSD and the simple version shown on page 177 of Kernighan and Ritchie  do not handle the append mode correctly. These versions do an lseek to the end of file when the stream is opened. To correctly support the append mode when multiple processes are involved, the file must be opened with the O_APPEND flag, which we discussed in Section 3.3. Doing an lseek before each write won't work either, as we discussed in Section 3.11.
When a file is opened for reading and writing (the plus sign in the type), the following restrictions apply.
Output cannot be directly followed by input without an intervening fflush, fseek, fsetpos,or rewind.
Input cannot be directly followed by output without an intervening fseek, fsetpos,or rewind, or an input operation that encounters an end of file.
We can summarize the six ways to open a stream from Figure in Figure.
3. Six ways to open a standard I/O stream
file must already exist
| || |
| || |
previous contents of file discarded
| || |
stream can be read
| || |
stream can be written
stream can be written only at end
| || |
| || |
Note that if a new file is created by specifying a type of either w or a, we are not able to specify the file's access permission bits, as we were able to do with the open function and the creat function in Chapter 3.
By default, the stream that is opened is fully buffered, unless it refers to a terminal device, in which case it is line buffered. Once the stream is opened, but before we do any other operation on the stream, we can change the buffering if we want to, with the setbuf or setvbuf functions from the previous section.
An open stream is closed by calling fclose.
int fclose(FILE *fp);
Returns: 0 if OK, EOF on error
Any buffered output data is flushed before the file is closed. Any input data that may be buffered is discarded. If the standard I/O library had automatically allocated a buffer for the stream, that buffer is released.
When a process terminates normally, either by calling the exit function directly or by returning from the main function, all standard I/O streams with unwritten buffered data are flushed, and all open standard I/O streams are closed.