In this chapter, we describe the standard I/O library. This library is specified by the ISO C standard because it has been implemented on many operating systems other than the UNIX System.

The standard I/O library handles such details as buffer allocation and performing I/O in optimal-sized chunks, obviating our need to worry about using the correct block size.

1 Streams and FILE Objects

In File I/O, all the I/O routines centered on file descriptors. When a file is opened, a file descriptor is returned, and that descriptor is then used for all subsequent I/O operations. With the standard I/O library, the discussion centers on streams. When we open or create a file with the standard I/O library, we say that we have associated a stream with the file.

With the ASCII character set, a single character is represented by a single byte. With international character sets, a character can be represented by more than one byte. Standard I/O file streams can be used with both single-byte and multibyte (‘‘wide’’) character sets. A stream’s orientation determines whether the characters that are read and written are single byte or multibyte. Initially, when a stream is created, it has no orientation. If a multibyte I/O function is used on a stream without orientation, the stream’s orientation is set to wide oriented. If a byte I/O function is used on a stream without orientation, the stream’s orientation is set to byte oriented. Only two functions can change the orientation once set. The freopen function will clear a stream’s orientation; the fwide function can be used to set a stream’s orientation.

When we open a stream, the standard I/O function fopen returns a pointer to a FILE object. This object is normally a structure that contains all the information required by the standard I/O library to manage the stream: the file descriptor used for actual I/O, a pointer to a buffer for the stream, the size of the buffer, a count of the number of characters currently in the buffer, an error flag, and the like.

2 Standard Input, Standard Output, and Standard Error

Three streams are predefined and automatically available to a process: standard input, standard output, and standard error. These streams refer to the same files as the file descriptors STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO, respectively.
These three standard I/O streams are referenced through the predefined file pointers stdin,stdout,and stderr. The file pointers are defined in the<stdio.h> header.

3 Buffering

The goal of the buffering provided by the standard I/O library is to use the minimum number of read and write calls.Also, this library tries to do its buffering automatically for each I/O stream, obviating the need for the application to worry about it.
Three types of buffering are provided:

  1. Fully buffered. In this case, actual I/O takes place when the standard I/O buffer is filled.
  2. Line buffered.
  3. Unbuffered.

Linux buffering characteristics:

  • Standard error is always unbuffered.
  • All other streams are line buffered if they refer to a terminal device; otherwise, they are fully buffered.

If we don’t like these defaults for any given stream, we can change the buffering by calling either the setbuf or setvbuf function.

At any time, we can force a stream to be flushed.

1
int fflush(FILE *fp);

The fflush function causes any unwritten data for the stream to be passed to the kernel.

4 Opening a Stream

The fopen, freopen, and fdopen functions open a standard I/O stream.

1
2
3
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 fd, const char *type);

An open stream is closed by calling fclose.

5 Reading and Writing a Stream

Once we open a stream, we can choose from among three types of unformatted I/O:

  1. Character-at-a-time I/O. We can read or write one character at a time, with the standard I/O functions handling all the buffering, if the stream is buffered.
  2. Line-at-a-time I/O. If we want to read or write a line at a time, we use fgets and fputs.
  3. Binary I/O. This type of I/O is supported by the fread and fwrite functions. For each I/O operation, we read or write some number of objects, where each object is of a specified size. These two functions are often used for binary files where we read or write a structure with each operation.

5.1 Character-at-a-time I/O

Three functions allow us to read one character at a time.

1
2
3
int getc(FILE *fp); 
int fgetc(FILE *fp);
int getchar(void);

In most implementations, two flags are maintained for each stream in the FILE object:

  • An error flag
  • An end-of-file flag

Output functions are available that correspond to each of the input functions we’ve already described.

1
2
3
int putc(int c, FILE *fp); 
int fputc(int c, FILE *fp);
int putchar(int c);

5.2 Line-at-a-Time I/O

Line-at-a-time input is provided by the two functions, fgets and gets.

1
2
char *fgets(char *restrict buf, int n, FILE *restrict fp); 
char *gets(char *buf);

The gets function should never be used. The problem is that it doesn’t allow the caller to specify the buffer size. This allows the buffer to overflow if the line is longer than the buffer, writing over whatever happens to follow the buffer in memory.

Line-at-a-time output is provided by fputs and puts.

1
2
int fputs(const char *restrict str, FILE *restrict fp); 
int puts(const char *str);

5.3 Binary I/O

If we’re doing binary I/O, we often would like to read or write an entire structure at a time.

1
2
3
4
size_t fread(void *restrict ptr, size_t size, size_t nobj,
FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj,
FILE *restrict fp);

6 Positioning a Stream

There are three ways to position a standard I/O stream:

  1. The two functions ftell and fseek.
  2. The two functions ftello and fseeko.
  3. The two functions fgetpos and fsetpos.

7 Formatted I/O

7.1 Formatted Output

Formatted output is handled by the five printf functions.

1
2
3
4
5
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);

7.2 Formatted Input

Formatted input is handled by the three scanf functions.

1
2
3
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);

8 Implementation Details

Under the UNIX System, the standard I/O library ends up calling the I/O routines that we described in File I/O. Each standard I/O stream has an associated file descriptor, and we can obtain the descriptor for a stream by calling fileno.

9 Temporary Files

The ISO C standard defines two functions that are provided by the standard I/O library to assist in creating temporary files.

1
2
char *tmpnam(char *ptr);
FILE *tmpfile(void);

10 Memory Streams

The standard I/O library buffers data in memory, so operations such as character-at-a-time I/O and line-at-a-time I/O are more efficient. We can provide our own buffer for the library to use by calling setbuf or setvbuf. Memory streams are standard I/O streams for which there are no underlying files, although they are still accessed with FILE pointers. All I/O is done by transferring bytes to and from buffers in main memory.

Three functions are available to create memory streams.

1
2
3
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
FILE *open_memstream(char **bufp, size_t *sizep);
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);