Basic input/output with printf and scanf

In order to write useful programs, we need to be able to interact with a user or with other programs. We have seen a couple uses of the printf function to print something to the terminal. The C standard library provides printf and a few similar functions for providing input and output from/to the terminal and from/to a file. Both of these are extremely useful.

Output using printf

Let’s go back to that line in our variables program:

printf("number = %d\n", number);

If you will recall, this statement printed “number = number” where number was whatever numerical value was stored in number. The printf function takes an arbitrary number of arguments. The first argument to the printf function is a format string. The format string is a string that tells printf how to format the output. The format string may contain any number of format specifiers that correspond to the arguments given to the printf function. A format specifier is a series of characters, starting with “%”, that tells printf how to format the data in the output.

In the simplest case, the format string contains no format specifiers and is simply printed to the screen verbatim; this was the case in our hello world program. In the case above, the first and only format specifier is “%d” which tells printf that the corresponding argument should be interpreted as a signed base-10 integer value. For more information on format specifiers, see the printf documentation

The printf function allows us to very output complex information in a readable way. As another example, suppose you were doing some 3-D geometry, and needed to output a vector. Assuming the vector’s coordinates are given by floating-point variables “x”, “y”, and “z”, you could output the vector with the following line:

printf("vector = [%f, %f, %f]\n", x, y, z);

Working with Files

Reading and writing files in C is very similar to the way you work with files on your computer. There are three basic steps:

  1. Open the file
  2. Read from or write to the file
  3. Close the file.

In C, files are represented by a file pointer which has the type FILE *. Files are opened and closed with the fopen and fclose functions respectively. Once you have an open file, the stdio.h header file provides several functions for reading from and writing to that file. As an example, let’s make a little different version of our hello world program:

#include <stdio.h>

int main(int argc, char **argv)
{
    FILE * file;

    /* Open the file "hello.txt" in write mode */
    file = fopen("hello.txt", "w");

    /* Write a message to the file */
    fprintf(file, "Hello File!");

    /* Be a good program and close the file */
    fclose(file);

    return 0;
}

If you compile and run the code above, it will write “Hello File!” to the file “hello.txt” instead of writing “Hello World!” to the terminal. First, notice the call to the fopen function. The fopen function takes two arguments: the name of the file to open, and a mode string. The mode string tells the computer if we want to read from the file, write to the file, or both. The newly opened file is then stored in the file variable.

The next line is a call to the fprintf function. The fprintf function is almost identical to the printf function except that it takes an additional argument: the file to be written to. You could also use fprintf with format specifiers just like we did with printf: fprintf(file, "number = %d\n", number). The C standard library actually provides eight different variants of the printf function, each with its own purpose.

It is important to know that files are a type of stream. When you read from or write to a stream, you start at the beginning and advance through the stream as you read/write. You can’t always arbitrarily skip around and you can almost never simply add or remove something in the middle. Thanks to modern word processors, we have gotten used to the idea of going to the middle of a file and making changes. Working with files in C is more like a typewriter where, if you want to add a sentence in the middle, you have to re-type the entire page.

Input Using scanf

Now that we’ve talked a little about writing files, let’s discuss reading them. Unfortunately, reading files is, in general, a very difficult problem. For this reason, most common file formats follow very strict rules. However, if you place appropriate restrictions on the problem, it becomes fairly simple. For the purposes of this discussion, we will use the scanf function. The scanf function is very similar to printf only for reading instead of writing.

Consider the following program:

#include <stdio.h>

int main(int argc, char **argv)
{
    float celsius;
    float fahrenheit;

    /* Print out a prompt for the user */
    printf("Enter a temperature in centigrade: ");

    /* Read the user's input */
    scanf("%f", &celsius);

    /* Convert the temperature */
    fahrenheit = (celsius * 9) / 5 + 32;

    /* Tell the user the converted temperature */
    printf("%.2f Celsius is equivalent to %.2f in Fahrenheit\n", celsius, fahrenheit);
}

We have seen most of this before, so we will just focus on the call to scanf. The format specifier here tells scanf to expect a floating-point number and store that in the variable pointed to by the first argument after the format string. Notice the & character before celsius in the call to scanf. This is what is called the address operator. We will discuss this operator more when we talk about pointers; for now, you need to know that you have to put it in front of arguments to scanf so that scanf can properly assign values to them.

As with printf, the C standard provides several variants of the scanf functions. For more details on using scanf, refer to the scanf documentation.

Escape Characters

Along the way, you may have noticed that I place “” at the end of most of my printf format strings. This is called an escape character In C strings, the “" character is special; whenever a”" is encountered, the “" together with the character that follows make up a single character. This allows you to insert characters that you cannot type on your keyboard or that would otherwise cause problems like the ‘“’ character. The following table lists some common escape characters:

Escape sequence Name Description
New line Causes whatever gets printed next to be on a new line
System Bell Makes the computer beep
Linefeed Used by typewriters to indicated the need to advance the paper
Backspace Backs up the terminal by 1 character
Carriage Return Causes the terminal to go back to the begining of the line
Horizontal Tab Writes a tab character
~\ Backslash Represents “". You have to use”~\” because “" denotes escape characters
" Double Quote Allows you to put a ‘“’ character inside quotes
' Single Quote Allows you to put a single quot inside single quotes
Octal character Allows you to enter a specific character value such as “\173” which is the same as “{”.
Hexadecimal Character Allows you to enter a specific character value such as “7b” which is the same as “{”.

The new line, linefeed, and carriage return characters go back to the days when your terminal was an actual physical typewriter. However, newline (and sometimes carriage return) are still used to indicate a new line of text.

Everything’s a File

One thing to know about UNIX systems in particular is that everything is a file. While this is not strictly true for Windows, it is to a certain extent. On a UNIX system such as Linux or MacOS, almost every form of input/output including the screen, files, network communication, and even sound is treated as a file. Specifically, the C language provides 3 special “files” for reading from and writing to the terminal: stdin, stdout, and stderr. If you include the stdio.h header file, these files are already available and you do not need to open or close them. Since these are provided by the C language, you can use them on Windows too.

The stdin file, also known as standard input allows you to read input from the terminal. The stdout and stderr files, also known as standard output and standard error respectively, allow you to write output to the terminal. Standard error used for error messages or other messages that are not part of the program’s normal output. This allows the terminal to separate errors from regular output if it choses to do so.

Calling the printf function is identical to calling fprintf with stdout as its first argument and calling scanf is identical to calling fscanf with stdin as its first argument. There is no special function equivalent to calling fprintf with stderr as the first argument.

More Reading

At this point, I would recommend you skim through the C++ Reference’s pages on the stdio.h header file. There is more information available there than I was able to provide in this short discussion of the topic. Specifically, there are more ways to work with files than just the printf and scanf families.