Input and Output
Input and output in C can be done through streams. A stream
can be thought of as a continuous sequence of characters coming into or
out of the computer. For instance, when you enter a number into
one of your programs, say, 12.34, the stream of characters '1', '2', '.',
'3', '4', '\n' is read in by the scanf function and assembled
into a float or double for your program to compute with.
Input streams may be read from; output streams may be
written to.
Standard Input and Standard Output
Two important streams are standard input and standard output.
They are represented in C programs by the names stdin and
stdout, respectively. Some functions automatically use one of
these streams, for instance printf and scanf.
Redirection
In most operating systems, standard input is usually the
keyboard and standard output is the text display. Input functions like
scanf use standard input, while output functions like printf
use standard output. Both of these streams can be redirected, i.e.,
they can be made to use different objects than the keyboard and screen.
At the command prompt, redirection of standard input to a file is done
using the < sign and redirection of standard output to a file
is done using the > sign. For instance, suppose your program is
called foo and it accepts some input and prints some output.
Normally, you would just invoke it by typing:
foo
at the command prompt. Input would be taken from the keyboard and output
printed to the screen. However, suppose you want foo to accept
input from a file called bar instead of from the keyboard. Then
you would type
foo < bar
Suppose you want input from the keyboard but output to a file called
baz. Then you would type
foo > baz
Maybe you want both input and output redirected. Then you could type:
foo < bar > baz
Redirection is typically done when a great deal of information needs to
be processed by a program, more than the user is willing to type or
read from the screen. It can also be used to record the output of a
program to a file for later inpection. There are other kinds of redirection
we will discuss later...
Some Stream Functions
getchar and putchar
Streams are simply continuous sequences of characters. They can be used
at the lowest level with functions that simply get or put a character from
a stream. Two functions for doing this with standard input and standard
output are getchar and putchar, respectively.
(Extra information you didn't want to know: getchar and
putchar aren't really functions; they are preprocessor
macros, but the distinction is irrelevant at this point.)
putchar prints a single character to the standard output.
For example, the following program prints the letter 'h' on the standard
output:
#include <stdio.h>
int main () {
/* print 'h' on stdout */
putchar ('h');
return 0;
}
getchar reads a single character from the standard input,
returning the character read. For example, this program reads a single
character from the standard input into a character variable:
#include <stdio.h>
int main () {
char c;
/* read a char from stdin into c */
c = getchar ();
return 0;
}
These functions are sometimes useful, as we will see later, but their smarter
cousins scanf and printf provide more functionality.
(Theoretically, you could write all your programs with getchar and
putchar, but it would be quite tedious writing code to
do numeric conversions that scanf and printf do
automatically.)
scanf and printf
We have already seen several instances of printf and scanf.
These functions (and their friends, fprintf, fscanf, sprintf,
and sscanf) are the workhorses of C input and output. They handle
all kinds of formatted input and output.
The format of a printf statement is this:
printf (format-string,expr1,
expr2,...);
The format-string is a character string constant describing how
the output will look. It can be as simple as "Hello, World!\n"
or can have embedded tokens giving the format with which to
print the expressions that are the arguments expr1, expr2
etc. For example,
printf ("An order of %i items will cost $%0.2f.\n", num_items, cost);
where num_items is an int and cost is a
float. It will print out something like
An order of 20 items will cost $12.34.
depending on the values of the variables. The %i token means
that the first argument after the format string is an int.
The %0.2f token says that the second argument is a float, and
to print two significant digits past the decimal point. You can see a list
of all the printf formats by typing man 3 printf from the
command line.
Expressions can also be used in printf statements, for example:
printf ("An order of %i items will cost $%0.2f.\n",
num_items, cost_per_item * num_items);
where cost_per_item is a float. The multiplication expression will
be evaluated and the result passed as an argument to printf.
You can use as many of these tokens as you like in a single statement;
just make sure the tokens match up with order and number of arguments given.
The following table gives a partial description of printf tokens
with examples:
Token Meaning Examples Comments
%f print floating point "%f" unformatted float
"%0.3f" print 3 digits after decimal
"%6.2f" 6 chars total, 2 after decimal
%i print int, decimal "%i" unformatted int
"%5i" 5 digits, with leading spaces
"%0.5i" 5 digits with leading 0s.
%d same as %i
%x print int, hexadecimal "%x"
%c print character "%c"
%li print long "%li" use for variables of type long
%s print string "%s" print a string
"%10s" 10 chars of a string, leading spaces
%% print percent sign "%%"
The format of a scanf statement is similar:
scanf (format-string, &
var1, &
var2,...);
var1, var2, etc. are the variables to be read in. Note that
now the arguments must be variables. (You can't easily read into an
expression unless it is something special called an lvalue; more
about this later.) Note also that the variables names are preceded by
the ampersand (&). Arguments in C are passed by value,
that is, the value of an argument is what the function sees, not the
argument itself. To allow scanf to modify the variables, we must
tell it the location in memory of the argument; the &
operator does this. If you like, you may think of it as a magic incantation
you must invoke to keep the demons of
"Segmentation fault (core dumped)" away. Later we will see how
to use & to help us write our own functions.
Analogous to printf, the format-string describes how the
input should look. For example:
scanf ("%d %f", &a, &x);
will read a decimal integer into the int variable a
and a floating point value into the float variable x.
There is a complication with scanf that doesn't show up with
printf; remember how all arithmetic in C is done as double?
Well, with printf, "%f" just means print out a
double; any floating point value that's an argument to
printf, even a single float variable by itself, will
first be converted to double before being passed to printf.
The same is not true with scanf, since all we are passing are
memory locations (pointers). Thus, we must specify whether a float
variable is long (double) or not (float). We do this
for doubles by preceding the 'f' by an 'l', i.e., "%lf", for
"long float." So, if a variable d is declared as double,
we read it in with
scanf ("%lf", &d);
Here are some tokens you can use with scanf. You can also use
format specifiers as with printf, but usually you want the
input format to be as loose as possible so a mistake in the input won't
hurt your program.
Token Meaning Examples Comments
%f read floating point "%f" unformatted float
%lf read double "%lf" unformatted double
%i read int, decimal "%i" unformatted int
%li read long, decimal "%li" unformatted int
%d same as %i
%x read int, hexadecimal "%x"
%c read character "%c"
%s read string "%s" read a string (not until later)
scanf returns an integer value telling how many items were
actually read successfully. You can check this value to see if it
equals the number of variables you're reading; if not, an error
has happened (e.g. some joker typed "hello" instead of an integer,
or the end of file was reached).
Sometimes you have to do something called "flushing the input stream."
This means reading characters from a stream until the next carriage
return is found. This is useful if you want to read past extraneous
characters, like carriage returns and ending spaces, that the user has
typed in. The stream will be "flushed" to the next line, and all keystrokes
(characters) received before that line will be ignored. Flushing the
input stream looks like this:
while (getchar () != '\n');
The while statement continues to execute until the condition
within parentheses is false, so this code will continue to read characters
one at a time from standard input until a carriage return is found. At
this point, the next thing in the stream will be the first thing on the
next line. Note that "flushing the stream" is not quite standard terminology
and many C programs you will see will deal with this situation in different
ways or not at all.
Other Streams
Later on we will see how to make our own streams. We can open files
not on the standard input or output or even open other devices such
as a printer, mouse, or tape drive. Once we have these stream
variables, we can print to them with fprintf and read from them
with fscanf. There are also many other functions for dealing
with generic streams.
C defines another output stream called standard error, or stderr.
It usually also goes to the text display, along with standard output.
You can write to standard error using the fprintf function,
e.g.
fprintf (stderr, "The value of the variable is %i.\n", a);
The format string is the second argument to fprintf; the first
is the name of the stream, stderr in this case. Standard error
is usually used to write error messages to. It is not redirected
even when standard output is, so you can have standard output go to a file
but standard error still go to the screen. It is useful in debugging your
programs; if you periodically write out the values of certain variables
to standard error, you can keep track of what your program is doing and
where it might be going wrong. Most programs like cc
write their error messages to standard error; this is why, even if you
type
cc -o foo foo.c > stuff
you will still see the error messages on the screen; they are on
standard error while only standard output is redirected to stuff.
To redirect both standard error and standard output from the C-Shell
(most of you are running the C-Shell for your prompt), use
>& instead of just >. For example, to
put a list of the error messages you get when compiling foo.c
into a file called stuff, do this:
cc -o foo foo.c >& stuff
You can also use fprintf to print to other streams. For
example,
fprintf (stdout, "something...", ...);
is equivalent to
printf ("something...", ...);
When we learn how to open our own streams, this (and fscanf)
will become more important.
Extra for Experts
You're not responsible for this stuff on a test, it's just nice to know...
Here's even more on redirection at the command prompt.
Suppose you want to send standard error and standard output from your
program foo to a file bar.
No problem, you just run your program like this:
foo >& bar
But what if you want standard error to go to one file, say, baz,
while standard input still goes to bar? Many people will tell
you this is impossible in C-Shell. Indeed, one of the faults of C-Shell
is that it doesn't directly provide this functionality. However, you
can work around this "feature" with something like this:
( foo > bar ) >& baz
The parentheses start a sub-shell that runs foo, sending the
standard output to bar. The standard error, which is the
only output that escapes the sub-shell, is redirected from there to
baz.
Suppose you are using a shell other than C-Shell, such as the Bourne Shell,
or Korn Shell. How do you redirect standard error? Use 2>.
For example, the following:
foo > bar 2> bar
does the same thing as the first example above, and this:
foo > bar 2> baz
does the same thing as the second example.
Linux is a multitasking operating system. This means that more
than one process can run at a time. Processes can communicate with
each other via things called pipes. You can use this from the
shell to allow your program to communicate with other programs.
The standard output of one program becomes the standard input of another
program. For example, the standard program more accepts input
from the standard input and pages it to the screen. This means
it prints it one screenful at a time, waiting for you to press the space
bar until the next screenful is printed. If your program has a lot of
output, you can page it by piping the output through more:
foo | more
The | symbol acts like the >, except the target
is another running program, not a file. In C-Shell, you can also pipe
standard error in a way similar to redirecting it to a file. Suppose you
want to see all your compiler errors one screenful at a time. Just
enter:
cc -o foo foo.c |& more
This pipes all the C compiler errors generated when compiling foo.c
through more.
Let's have some more fun with this. The standard program ls
lists the files in your directory; when called with the -l
option, it will give a long listing that includes the size in bytes of
each file in the fifth field. The program sort sorts
lines from the standard input; with the +4 -rn option,
it sort them by the fifth field (it starts counting at 0, so field
#4 is the fifth field) in reverse numeric order. So the following:
ls -l | sort +4 -rn
will give you a listing of all your files, sorted in decreasing order
of their sizes. If you have lots of files, just do this:
ls -l | sort +4 -rn | more
The files are listed, sorted, then the output is piped through more
so you can easily page through the listing.