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.
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);
Reading and writing files in C is very similar to the way you work with files on your computer. There are three basic steps:
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.
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.
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.
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.
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.