Skip to content

Input and Output#

Streams#

C handles input and output using streams. One can think of them as three pipes. We have access to one end of each pipe and the operating system has access to the other:
- Whatever is placed at the other end of an input stream, makes its way to our end.
- Whatever we place at our end of an output stream, makes its way to the operating system's end.

Functionality for operating with streams is provided by the <stdio.h> header.

Input#

Reading from an input stream is most commonly done using the fscanf function:

int fscanf(FILE * restrict stream, const char * restrict format, ...);

The fscanf function reads from the stream pointed to by stream, interprets the read data according to format and stores it using the subsequent arguments as pointers to the locations which should receive the converted data.

The format string can be comprised of three types of directives: whitespace characters, non-whitespace characters and conversion specification. The fscanf function goes through these directives one by one and tries to interpret the data from stream accordingly. It terminates once it has gone through all directives or if a directive fails to match data from stream:
- A directive composed of one or more whitespace characters tells fscanf to read and discard the data from stream until a non-whitespace character is encountered in stream or there are no more characters to be read. Such directives never fail.
- A directive which is a sequence of \(n\) ordinary, non-whitespace characters tells fscanf read the next \(n\) characters from stream and compare them with the sequence in format. If the sequences do not match, then the directive fails, but the differing and subsequent characters remain in stream. The directive also fails if an EOF is reached or there is an encoding error or other read error.
- A conversion specification directive is used to match a pattern of characters in stream and interpret them in a specific way so as to be stored at the locations specified by the additional arguments.

A conversion specification has the following syntax:

%[*][width][length]specifier

The fields in [] are optional and are not literally separated by brackets in the format string.

If the * is present, then no argument should be provided for this conversion specification. The execution of fscanf remains unaltered, but the interpreted value is discarded and not stored anywhere.

The width field is a positive decimal integer which specifies the maximum number of characters to read as part of this conversion specification.

The specifier field is mandatory and describes the pattern to be matched:

Value of specifier Description
i Matches any number of digits, optionally preceded by a sign (+ or -). The format is the same as expected by strtol with base equal to 0. By default, the digits are assumed to be decimal (0-9), but if they start with 0, they are treated as octal digits (0-7) and if they start with 0x, they are treated as hexadecimal digits (0-f). By default, the argument is treated as a pointer to int.
d Matches any number of decimal digits (0-9), optionally preceded by a sign (+ or -). The format is the same as expected by strtol with base equal to 10. By default, the corresponding argument is treated as a pointer to int.
u The same as d, but the argument is treated as a pointer to unsigned int by default.
b Matches any number of binary digits (0-1), optionally preceded by a sign (+ or -). The format is the same as expected by strtoul with base equal to 2. By default, the corresponding argument is treated as a pointer to unsigned int.
o Matches any number of octal digits (0-7), optionally preceded by a sign (+ or -). The format is the same as expected by strtoul with base equal to 8. By default, the corresponding argument is treated as a pointer to unsigned int.
x, X Matches any number of hexadecimal digits (both 0-f and 0-F), optionally preceded by a sign (+ or -). The format is the same as expected by strtoul with base equal to 16. By default, the corresponding argument is treated as a pointer to unsigned int.
a, A, e, E, f, F, g, G Matches an optionally signed floating-point number, infinity or NaN. The format is the same as expected by strtod. By default, the corresponding argument is treated as a pointer to float.
c Matches a sequence of characters whose number is equal to the width field. If width is not present, it matches a single character. By default, the corresponding argument is treated as pointer to char, signed char, unsigned char or void that points to a large enough chunk of memory to store the read sequence. No null terminator is appended.
s Matches a sequence of non-whitespace characters. By default, the corresponding argument is treated as pointer to char, signed char, unsigned char or void that points to a large enough chunk of memory to store the read sequence and a null terminator, which is added automatically.
[characters] Matches any non-empty sequence of characters which is composed only of the characters in characters. If we want to include ] in characters we must always place it directly after the opening [. By default, the corresponding argument is treated as pointer to char, signed char, unsigned char or void that points to a large enough chunk of memory to store the read sequence and a null terminator, which is added automatically.
[^characters] Matches any non-empty sequence of characters which is composed only of the characters not in characters. If we want to include ] in characters we must always place it directly after the opening [^. By default, the corresponding argument is treated as pointer to char, signed char, unsigned char or void that points to a large enough chunk of memory to store the read sequence and a null terminator, which is added automatically.
n No data is read from stream. Instead, the total number of characters read by the call of fscanf so far is stored at the location specified by the argument, which is treated as a pointer to a signed int.
% Matches a single % character in exactly the same manner as a directive which matches non-whitespace characters.
p Matches a sequence which can be produced by the %p conversion specification of fprintf. By default, the corresponding argument is treated as a pointer to a void pointer.

The length field can be used to modify how the corresponding argument of the conversion specification is to be treated:

Value of length Description
hh Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to unsigned char or signed char.
h Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to short int or unsigned short int.
l Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to long int or unsigned long int.

Specifies that the argument of an a, A, e, E, f, F, g or G conversion specification should be a pointer to double.

Specifies that the argument of a c, s, [characters] or [^characters] conversion specification should be a pointer to wchar_t.
ll Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to long long int or unsigned long long int.
j Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to intmax_t or uintmax_t.
z Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to size_t.
t Specifies that the argument of a b, d, i, o, u, x, X or n conversion specification should be a pointer to ptrdiff_t.
L Specifies that the argument of a a, A, e, E, f, F, g or G conversion specification should be a pointer to long double.
H Specifies that the argument of a a, A, e, E, f, F, g or G conversion specification should be a pointer to _Decimal32.
D Specifies that the argument of a a, A, e, E, f, F, g or G conversion specification should be a pointer to _Decimal64.
DD Specifies that the argument of a a, A, e, E, f, F, g or G conversion specification should be a pointer to _Decimal128.

The fscanf function returns EOF if it fails before any conversion specification is completed. Otherwise, it returns the number of input assignments it performed, which is greater than or equal to zero.

Output#

The most common way to write to a stream is using the fprintf function:

int printf(const char *format, ...);

The printf function takes a string format and a variable number of arguments. The string format is what ultimately gets written to stdout, but it is first interpreted and modified by printf. This allows us to provide additional data to printf and also specify how this data should be formatted.

This formatting is done using format specifiers in format. The first additional argument corresponds to the first specifier, the second argument to the second specifier and so on. Therefore, the number of additional arguments must be at least as much as the number of specifiers. Additional arguments are ignored.

These specifiers have the following syntax:

%[flags][width][precision][length]specifier

Everything in [] is optional. The fields themselves have the following possible values:

specifier Interpret and format as Example Output
d, i Signed decimal integer -392, 392
u Unsigned decimal integer 7235
o Unsigned octal integer 610
x Unsigned hexadecimal integer, lowercase 7fa
X Unsigned hexadecimal integer, uppercase 7FA
f Decimal floating point, lowercase 392.65, nan, inf
F Decimal floating point, uppercase 392.65, NAN, INF
e Decimal scientific notation, lowercase 3.9265e+2
E Decimal scientific notation, uppercase 3.9265E+2
g The shortest of e or f 392.65
G The shortest of E or F 392.5
a Binary scientific notation, represented using hexadecimal digits, lowercase 0xc.90fep-2
A Binary scientific notation, represented using hexadecimal digits, uppercase 0XC.90FEP-2
c Single character a
s Null-terminated string of characters sample
p Memory address b8000000
n The corresponding argument must be a pointer to signed int. Prints nothing but the total number of characters written so far is stored in the provided location.
% Writes a single % %

The width field is optional and can be used to specify the minimum number of characters to be printed:

width Description
Number Minimum number of characters to be printed. If the value to be printed is shorter than this number, blank spaces are additionally printed.
* The number of minimum characters is specified in an additional argument which preces the argument to be formatted.

The precisionfield is used to specify the precision with which the number is to be printed:

precision Description
.number For the specifiers d, i, o, u, x, X this specifies the minimum number of digits to be written. If the value to be written is shorter, leading zeros are added. If number is \(0\) and the argument is also \(0\), then nothing is written.

For the specifiers a, A, e, E, f, F this specifies the number of digits to be printed after the decimal point (6 by default).
.* The precision is specified in an additional integer value argument preceding the argument to be formatted.

For the specifiers g and G this is the maximum number of significant digits to be printed.

For the specifier s this is the maximum number of character to write. The defaut is to write until the null terminator is reached.

If only the dot (.) is present, then an implicit number of \(0\) is assumed.

The length field specifies as what data type the argument should be interpreted:

length specifier
d, i u, o, x, X f, F, e, E, g, G, a, A c s p n
None int unsigned int double int char* void* int*
hh signed char unsigned char signed char*
h short int unsigned short int short int*
l short int unsigned short int wint_t wchar_t* long int*
ll long long int unsigned long long int long long int*
j intmax_t unitmax_t intmax_t*
z size_t size_t size_t*
t ptrdiff_t ptrdiff_t ptrdiff_t*

Standard I / O#

C provides three standard streams for input and output: standard input (stdin), standard output (stdout) and standard error (stderr).
- Whatever is placed at the other end of stdin, makes its way to our end.
- Whatever we place at our end of stdout or stderr, makes its way to the other end, respectively.

Most commonly, all three streams end up in the console, but it may also be a file or a network socket and it is also not necessary that all streams end up in the same place.

Standard Input#

Reading from stdin is most commonly done using the scanf function:

int scanf ( const char * format, ... );

It behaves in the same way as fscanf, but stream is automatically set to stdin.

Standard Output#