One of the most important and common tasks in virtually every program is the printing of output. Programs use output to request input from a user, to display status messages, and to inform the user of the results of computations that the program performs. For obvious reasons, the manner in which the program displays its output can have a profound effect on the usefulness and usability of a program. When a program prints its output in a neatly formatted fashion, the output is always easier to read and understand. As a result, being able to write programs easily that produce attractive output is an essential feature of most programming languages.
C has a family of library functions that provide this capability. All of these functions reside in the stdio library. Although the library contains several functions for printing formatted output, it is likely that you will only use two of them with any frequency. One, the printf (short for "print formatted") function, writes output to the computer monitor. The other, fprintf, writes output to a computer file. They work in almost exactly the same way, so learning how printf works will give you (almost) all the information you need to use fprintf.
To use the printf function, you must first insure that you have
included the stdio library in your program. You do this by placing the C
preprocessor directive
#include <stdio.h>
at the beginning of your program. If you are also including other libraries, you will have
other #include directives; the order of these does not
matter.
When you actually use the printf function, that is, when you want your program to print something on the screen, you will place a call to the function in your source code. When we want a function to perform its task we say we "call" it, and a function call is a type of program statement. All function calls have the same basic format. The first part of the call is always the name of the function (in this case, printf). Following the function name is an argument list or parameter list. The terms "argument" and "parameter" are synonymous in computer programming. An argument list provides information for the function. In the same way that a mathematical function takes some input value, performs a transformation, and produces a result (output), functions in C typically require some input as well. For the printf function, the argument list will provide the information that the function should print to the screen. We generally say that we pass arguments to a function.
An argument list has a particular form as well. First, a pair of parentheses always
encloses the list of arguments. Inside the parentheses, we separate multiple arguments
from each other with a comma. Since a function call is a kind of statement, you will also
need to follow the call with a semicolon, just after the closing parenthesis of the
argument list. We can formalize a function call then as follows:
function_name (argument1, argument2, ...);
The "..." signifies that there may be more arguments. In fact, there may also be
fewer arguments.
Different functions require different kinds of input. Some functions need only one parameter, others may need many, and some do not need any parameters at all. In this last case, the argument list would simply be a pair of parentheses with nothing inside. In most cases, any given function needs a particular number of parameters. That is, we might have a function that requires three pieces of information to perform its task. Every time we call that function, we will need to provide exactly three parameters in its argument list. The printf function is unusual, because the number of parameters it needs is variable. It always requires at least one argument, called the format string. Depending on what this argument contains, we may need to pass other parameters to printf as well.
In C, a "string" is a sequence of one or more characters that the programmer intends to use as a unit (such as a word or sentence). The first program you saw printed a sentence, "Hello world," to the screen. The series of characters that forms this sentence is a string. To ensure that the compiler processes a string as a string, rather than as identifiers, we use a pair of double quotes around a string constant to show that it is a string. This is quite similar to using single quotes to indicate a character constant. It is important to remember that the single and double quotes are not interchangeable. Later, you will learn the difference between a one-character string, such as "a" and a single character, such as 'a'. For now, you must simply try to remember that they are different. The following mnemonic may help you remember when to use double quotes and when to use single quotes. A character is always just one character, while a string usually contains several characters. Thus, a character is usually "shorter" than a string. The single quote is also "shorter" than the double quote, so you use the "short" with the "short."
In its simplest form, a format string is just a series of characters that you want to
print to the screen. It will print exactly as it appears in the argument list, except that
the double quotes will not appear. This is generally how you would use printf
to print a prompt to request that the user of a program enter some data. For example, if
you wanted to ask a user to type a number, you might call printf as
follows:
printf ("Please type an integer then press Enter: ");
When the computer executes this statement, the message will appear on the screen:
Please type an integer then press Enter: |
You will often use this simplest kind of format string when you want to display some
sort of status message or explanation on the screen. For instance, if you wrote a program
that you knew would take some time to perform a task, you would probably want to let the
user know that the program was working and had not crashed. You might use printf
to tell the user what the program is doing:
printf ("Searching, please wait...");
On the screen, you would see:
Searching, please wait... |
In both of the examples above, once the message has appeared, the cursor will remain at the end of the printed output. If you called printf again, the next message would appear immediately after the first one. Usually, this will not be what you want. Instead, you will want to print the next message on the next line of the screen, but you will need to tell printf to do this; it will not happen automatically. You know that if you are typing that you press the Enter key to get from one line to the next, but, as a programmer, if you press the Enter key inside the double quotes of the format string, the cursor will go to the next line of your source code file. If you then type the double quote, closing parenthesis, and semicolon and then try to compile the program, the compiler will give you a syntax error. Usually, the error message will tell you that you have an "unterminated string constant." This is because the compiler expects to find the closing double quote on the same line as the opening double quote.
Obviously, then, we need another way to tell printf to send the cursor to the next line after printing the rest of the characters in the format string. C uses escape sequences within a format string to indicate when we want printf to print certain special characters, such as the character that the Enter key produces. The escape character for a newline (which sends the cursor to the beginning of the next line on the screen) is \n. The backslash is called the escape character in this context and it indicates that the programmer wants to insert a special character into the format string. Without the backslash, printf would simply print the 'n'. You might guess that the 'n' is an abbreviation for "newline." C provides several escape sequences, but only a few are common. Others that you might find useful appear in the following table:
Escape sequence | Action |
\n | prints a newline |
\b | prints a backspace (backs up one character) |
\t | prints a tab character |
\\ | prints a backslash |
\" | prints a double quote |
If we alter the second example above as follows:
printf ("Searching, please wait...\n");
the screen will appear as before, except that now the cursor will be on the next line.
Furthermore, if the program contains another printf statement later on,
the next output will be printed on that same next line.
Searching, please
wait... |
Here are a few more examples of printf statements that make use of
escape sequences:
printf ("\nName\tAddress\n");
produces:
Name Address |
printf ("Joe's Diner\b");
displays:
Joe's Dine |
printf ("Please type \"Yes\" or
\"No\": ");
prints:
Please type "Yes" or "No": |
As you can see, escape sequences give us some ability to format output even when we use
the simplest form or the printf function, but printf is
actually much more powerful than we have seen. First, assume for a moment that you have
declared a variable in your program as follows:
int number = 10;
Now suppose that you want to prove to yourself that the variable number
really does hold the value 10. You want to display the string "The value of number is
10" on the screen. You might try calling printf as follows:
printf ("The value of number is number\n"); /*
This won't work */
Of course, because printf prints the format string exactly as it appears
(except for the escape sequences), what you will see on the screen is:
The value of number is
number |
The problem is that within the program's source code, we have only one way to refer to the variable number and that is by its name. The names of identifiers are made up of characters, so if we place a variable's name in a format string, printf will simply print that name. Just as C solves the problem of displaying special characters within a format string through the use of escape sequences, it solves this problem using another special notation within the format string. Besides escape sequences, the format string argument can also contain format specifiers. A format specifier is a placeholder that performs two functions. First, it shows where in the output to place an item not otherwise represented in the format string and it indicates how printf should represent the item. The "item" is most often a variable, but it can also be a constant. In particular, format specifiers allow us to print the values of variables as well as printing their names.
All format specifiers begin with a percent sign, just as all escape sequences begin
with a backslash. What follows the percent sign tells at least what data type printf
should expect to print. It can also indicate exactly how the programmer wants printf
to display it. Schematically, we can represent the syntax for a format specifier as
follows:
%[flags][width][.precision]type_character
As usual, italics indicate placeholders for which a programmer must substitute a
specific value. The square brackets that appear in this syntax template mean that the
placeholders within are optional. They can appear or not, as the needs of the programmer
dictate. The brackets themselves do not appear in the source program. Most of the time,
format specifiers will not include any of the optional items. The options give the
programmer precise control over the spacing of the output and even over how much of the
output printf will display. Thus, most often a format specifier will
simply be a percent sign followed by a "type character." The type character is
what tells printf what data type to print. The following table shows the
most common type characters (several others exist):
type character | print format |
d | integer number printed in decimal (preceded by a minus sign if the number is negative) |
f | floating point number (printed in the form dddd.dddddd) |
E | floating point number (printed in scientific notation: d.dddEddd) |
g | floating point number (printed either as f or E, depending on value and precision) |
x | integer number printed in hexadecimal with lower case letters |
X | integer number printed in hexadecimal with upper case letters |
c | character |
s | string |
For example, the format string "%d" indicates to printf that
it should write an integer in base 10 format, whereas the format string "%s"
tells printf to print a string. Notice that the format specifiers tell
what kind of thing the programmer wants to display, but they do not tell what value
to print. That means that printf will need some additional information in
the form of an additional argument. A format string can contain more than one format
specifier and the format specifier(s) can appear in conjunction with other text, including
escape sequences. Each format specifier that appears in the format string requires an
additional argument in the argument list. The additional argument specifies what value printf
should substitute for the format specifier. For instance, the following call to printf
will display the value of our variable number:
printf ("The value of number is %d\n", number);
The value of number is
10 |
The two arguments to this call to printf combine to tell the
function exactly what to write on the screen. What happens is that the printf
function actually takes apart the format string and checks each character before
displaying it. Whenever it encounters the percent sign, it checks the next character. If
the next character is a type character, printf retrieves the next
argument in the argument list and prints its value. If the next character is not
a type character, printf simply displays the percent sign. For example,
printf ("The value of number is %q\n", number);
will print:
The value of number is
%q |
Although it is unlikely that you will ever really want to do so, you may be asking,
"so what if I want to print something like '%d' on the screen." If printf
finds the sequence %d in a format string, it will substitute a value for it. To print one
of the format specifiers to the screen, then, you have to "trick" printf.
The following call:
printf ("The % \bd format specifier prints a base 10
number.\n");
will display:
The %d format specifier
prints a base 10 number. |
In the format string, a blank space follows the percent sign, not a type character, so printf will simply display the percent sign. It then displays the blank space (the next character in the format string), and then it displays the backspace character (specified with the escape sequence \b). This effectively erases the blank space. The next character in the format string is the letter 'd', which printf writes in the place where it originally wrote the blank.
We mentioned above that we can have more than one format specifier in a format string.
If we do this, we will need one additional argument for each of the format specifiers.
Furthermore, the arguments must appear in the same order as the format
specifiers. Examine the following examples:
printf ("The value of number, %d, multipled by 2, is
%d.\n", number, number*2);
prints:
The value of number, 10,
multiplied by 2, is 20. |
As you can see from the previous example, the argument can be an expression such as number
* 2 as well as a variable.
printf ("The value of number, %d, multipled by 2, is %d.\n", number*2, number);
displays:
The value of number, 20,
multiplied by 2, is 10. |
In the preceding example, the order of the additional parameters is wrong, so the
output is plainly nonsensical. You must remember that printf does not
understand English, so it cannot determine which argument belongs with which format
specifier. Only the order of the parameters matters. The printf function
substitutes the first of the additional arguments for the first format specifier, the
second of the additional arguments for the second format specifier, and so on. Thus,
printf ("%d + %d = %d\n", number, number*2, number + number*2);
writes:
10 + 20 = 30 |
Because printf uses the additional parameters to give it the values to
substitute for the format specifiers, it is essential that, as a programmer, you supply
enough parameters. For instance, if we rewrite the preceding example as follows so that we
have three format specifiers in the format string, but only two additional parameters:
printf ("%d + %d = %d\n", number, number*2);
printf will print something bizarre on the screen. Since it has no
value for the third format specifier, it will print a garbage value. We have no way to
predict exactly what value it will display (it may even coincidentally be the right
value!), but it would not be surprising to see output such as:
10 + 20 = -4797 |
The moral of the story is that if you see strange output when you are using format specifiers, one of the first things you should check is the order and number of the additional arguments.
You might also see unexpected output for one other reason. The type character in the
format specifier determines completely how printf will display a value.
If you include a format specifier with a particular type character in your format string
and then give an argument of a different data type, printf will display
the value of the argument using the syntax for values corresponding to the type character,
not the syntax corresponding to the data type of the argument. For example,
printf ("%d", 'a');
will print:
97 |
since the ASCII code for 'a' is 97.
printf ("%f", 5);
will display:
5.000000 |
In each example, printf makes an implicit type conversion of its argument to force it to agree with the data type that the type character specifies. In some special cases, you can use this automatic conversion to your advantage, but most of the time, you will want to ensure that the data type of the argument matches the type character.
The syntax template for a format specifier given above provides for several options.
These options allow the programmer to control precisely how output will appear on the
screen. The syntax template appears again here for easy reference:
%[flags][width][.precision]type_character
Again, the order of the options is just as important as the order of arguments to the printf
function. In other words, if you want to use multiple options, they must appear in the
same order as they do in the syntax template.
Although the first option is flags, it is actually the least common, so we
will leave its discussion for last. The width option is the option you will use
most frequently. This option is often called a field width. We usually use field
widths to line up columns of data to form tables on the screen. Printing things in tables
makes output more readable. We can also use field widths for other reasons as well. Since
they allow us to control exactly where a value will appear on a line of the screen, we can
also use field widths when we want to "draw" with characters. The value that the
format specifier indicates is sometimes called a "field" so the field width
controls how many columns the field will occupy on the screen. The value that we
substitute for the italicized width in an actual format specifier can be one of
two things. Most often, it will be an integer. For example, assume that your program
contains a declaration for a character variable as follows:
char digit = '2';
If the body of the code contains the following statement:
printf ("%3c\n", digit);
what printf will display is:
2 |
Notice that two blank spaces precede the digit '2'; this means the entire field (two
blanks and one non-blank character) occupies three columns. In addition, notice that the
blanks come before the digit. We call this right justification. Right
justification implies that items on a single row of the screen will line up along the
right margin. Thusm if we immediately make another call to printf as
follows:
printf ("%3c\n", digit+1);
the screen will look like this:
2 3 |
The field width you specify is the minimum number of columns that output will
occupy. If a complete representation of a value requires more columns, printf
will print the whole value, overriding the field width. For example, consider the
following call to printf:
printf ("%3f\n", 14.5);
The field width specifier in this call is 3, but the value, 14.5 actually requires four
columns (the decimal point requires a column as well as the digits. In this case, the
display will be:
14.5 |
If we alter the call:
printf ("%3f\n%3f", 14.5, 3.2);
printf writes:
14.5 3.2 |
Notice that because the field width was too small, the right justification fails; the two numbers do not line up along the right hand margin. A field width that requires fewer columns than the actual data value requires will always result in this failure.
You may be wondering at this point how justification allows us to print tables. So far,
we have only put one value on each line, but if we print several values before each
newline, we can use field widths to force them to line up. Consider the following series
of printf statements:
printf ("%10s%10s\n", "month",
"day");
printf ("%10d%10d\n", 1, 10);
printf ("%10d%10d\n", 12, 2);
What appears on the screen when these statements executes is a table:
month day 1 10 12 2 |
Notice in particular that the field widths always start from the last column printed. At the beginning, the cursor is in the extreme upper left corner of the screen. The format string in the first printf statement specifies that the function should print the string "month" using 10 columns. Since "month" only requires 5 columns, printf precedes the string with five blanks. At this point, the cursor will be in the eleventh column. The next format specifier requires printf to write the string "day", also using 10 columns. The string "day" takes up three columns, so printf must precede it with seven blanks, starting from the current cursor position. In other words, the column count begins just after the last letter of the string "month".
In a situation like this, where we are printing only constants, we could simply embed the blanks in the format string and not bother with format specifiers or additional arguments. The sequence of statements:
printf (" month day\n"); printf (" 1 10\n"); printf (" 12 2\n");
would produce exactly the same output. In most situations, however, the additional arguments will be variables. As such, we cannot necessarily determine what values they will hold when the program executes. In particular, when we have numeric variables, we will not be able to predict whether the value of the variable will be a one-digit number or a 4-digit number. Only by using field widths can we guarantee that the columns will line up correctly.
Occasionally, when we write a program, we cannot even predict how large a field width
we will need when a program executes. This implies that the field width itself needs to be
a variable, for which the program will compute a value. Although it is rare for this
situation to arise, it is worth mentioning how you can accomplish this. Just as was the
case when we wanted to print the value of a variable, if we try to use a variable's name
as a field width specifier, printf will simply print the name to the
screen. For example, assume that you have declared an integer variable named width
and have somehow computed a value for it. The call:
printf ("%widthd\n", 10);
will print:
%widthd |
To solve this problem, C uses an asterisk in the position of the field width specifier
to indicate to printf that it will find the variable that contains the
value of the field width as an additional parameter. For instance, assume that the current
value of width is 5. The statement:
printf ("%*d%*d\n", width, 10, width, 12);
will print:
10 12 |
Notice that the order of the additional parameters is exactly the same as the order of the specifiers in the format string, and that even though we use the same value (width) twice as a field width, it must appear twice in the parameter list.
Precision specifiers are most common with floating point numbers. We use them, as you
might expect from the name, to indicate how many digits of precision we want to print.
Compilers have a default precision for floating point numbers. The help facility or user's
manual for your compiler will tell you what this default is, although you can also
determine its value by simply printing a floating point number with a %f format specifier
and counting how many digits there are after the decimal point. Quite often, we want to
control this precision. A common example would be printing floating point numbers that
represent monetary amounts. In this case, we will typically want just two digits after the
decimal point. You can see from the syntax template that a period must precede the
precision specifier. The period really is a syntactic device to help the compiler
recognize a precision specifier when no field width exists, but the choice of a period
serves to help remind the programmer that it represents the number of places after a
decimal point in a floating point number. Just as for the field width specifier, the
programmer may use a number or an asterisk as a precision specifier. The asterisk again
indicates that the actual value of the precision specifier will be one of the additional
parameters to the printf call. For example,
printf ("%.2f\n", 3.675);
will print:
3.68 |
Notice that printf rounds the number when a precision specifier
requires that some digits must not appear.
printf ("%.*f\n", width, 10.4);
assuming that the current value of width is 3, will print:
10.400 |
Precision specifiers have no effect when used with the %c format specifier. They do
have an effect when printing integers or strings, however. When you use a precision
specifier with integer data, one of two things may happen. If the precision specifier is
smaller than the number of digits in the value, printf ignores the
precision specifier. Thus,
printf ("%.1d\n", 20);
will print the entire number, "20". Here, the precision specifier is less
than the number of digits in the value. On the other hand, if the precision specifier is
larger than the number of digits in the value, printf will
"pad" the number with leading zeros:
printf ("%.4d\n", 20);
will print "0020".
With string data, the precision specifier actually dictates the maximum field
width. In other words, a programmer can use a precision specifier to force a string to
occupy at most a given number of columns. For instance,
printf ("%.5s\n", "hello world");
will print only "hello" (the first five characters). It is fairly rare to
use precision specifiers in this fashion, but one situation in which it can be useful is
when you need to print a table where one column is a string that may exceed the field
width. In this case, you may wish to truncate the long string, rather than allow it to
destroy the justification of the columns. Generally, when this happens, you will use both
a field width specifier and a precision specifier, thus defining both the maximum and
minimum number of columns that the string must occupy. Thus, if name is a
variable that contains a string,
printf ("%10.10s\n", name);
will force printf to use exactly 10 columns to display the value
of name. If the value is less than ten characters long, printf
will pad with leading blanks; if the value has more than ten characters, printf will
print only the first ten.
Flags are fairly uncommon in format specifiers and although several flag options exist, you are most likely to use only two of them. The first is a minus sign, which you will use in conjunction with a field width specifier. By default, whenever printf must pad output with blanks to make up a field width, the blanks precede the representation of the data. This results in right justification, as previously mentioned. In a few situations, you may wish to left justify data. That is, you may want values to line up along the left side, rather than the right side. In most cases, it will be best to right justify numbers and left justify strings. For instance, if you wanted to print a table of student names followed by their test scores, you would probably want the table to appear as follows:
Mary Jones 89 Sam Smith 100 John Cook 78
The names are lined up along the left, while the scores are lined up along the right.
To print a single line of this table, your format string might look as follows:
"%-15.15s%4d"
The minus sign indicates left justification for the name, which must occupy exactly
15 columns. The %4d format specifier tells printf to right justify the
score and to have it occupy four columns. Since we can assume that no score will actually
be larger than a three digit number, specifying a field width of four ensures that we will
have a blank space between the name and the score.
The other flag that you may want to use is a plus sign. This flag is only meaningful
with numeric data. By default, when printf displays a number, it prints a
minus sign for negative numbers, but it does not print a plus sign for positive numbers.
If you want the plus sign to appear, this flag will cause printf to
display it. Thus,
printf ("%+.3f", 2.5);
will print "+2.500".