
Every program so far has printed one line. printf can do a good deal more - numbers, characters, whole strings, laid out in columns - and it does it with a little language of its own inside the quotes. This section is that language, where printf comes from, and the two places where it will do something you did not mean.
printf Comes FromOne thing first, because it explains the line at each end of every program. printf is built into this compiler: it is part of the 4,352 bytes of runtime that every program carries (S6), and a program that uses nothing else needs no #include at all. But printf is the exception. Almost everything else a C program reaches for - strlen for the length of a string, atoi to turn typed digits into a number, malloc for memory - is not built in. It is C, in STDIO.LIB on the disc, and the compiler reads it from there.
The question marks in #include ?STDIO.LIB? say how to read it: go through the library, find the functions this program has called and not defined, and compile those and only those. That is why the line goes last - a function called below it has not been called yet when the library is read, and the compiler ends with undefined symbol strcpy or whatever it was - and why the same name without the question marks, #include STDIO.LIB, is a different thing altogether: it compiles the whole library, 4,876 bytes of it, into your program whether you use it or not.
STDIO.H, the small file at the top, is the names the library and the course rely on: TRUE and FALSE, EOF, NULL, void, and FILE (S20). It goes first so that they exist by the time anything uses them.
printf takes a string and, after it, any number of values. The string is printed as it is, except that wherever it contains % and a letter, the next value is printed instead, in the form the letter says:
| Letter | Prints the value as |
|---|---|
%d | a number, signed: -1 |
%u | a number, unsigned: the same bits as 65535 |
%x or %X | a number in hexadecimal, capitals: FF |
%o | a number in octal: 377 |
%c | a single character, from its code: 65 is A |
%s | a string |
A width can go between the % and the letter. %5d prints the number in a field five wide, padded on the left with spaces; %-5d pads on the right; %05d pads with zeros. A field too narrow for its value is widened, never cut. For a string, %.2s prints at most two characters of it. And %% prints a % sign.
#include STDIO.H
main()
{
printf("Einstein\n");
printf("%d K of memory\n", 64);
printf("%d and %d\n", 7, 42);
printf("[%5d] [%-5d] [%05d]\n", 42, 42, 42);
printf("%c/%d\n", 'C', 80);
printf("%s has %d letters\n", "Brick", 5);
printf("%d in hex is %x, in octal %o\n", 255, 255, 255);
printf("100%% done\n");
}
#include ?STDIO.LIB?
Two new things in it. 'C' with single quotes is a character constant: not a string but a single character's code, the number 67, so %c prints it as the letter and %d would print 67. And "Brick" in the argument list is a string being passed to %s, exactly as the format string is passed to printf.
Starting from
Typed as PRINT.C; HC PRINT.C, PRINT.
What you should see
Einstein
64 K of memory
7 and 42
[ 42] [42 ] [00042]
C/80
Brick has 5 letters
255 in hex is FF, in octal 377
100% done
printf takes the values one at a time, matching each % to the next one. It does not check that they agree, and it does not check that there are enough. Two consequences, each of which you will meet:
A % with no value prints rubbish. This program:
printf("%d %d %d\n", 1, 2);
printf("%d\n", 1, 2, 3);
prints
1 2 8
1
The third %d had nothing to print, so it printed whatever was lying in memory where a third value would have been - 8, this time; anything, another time - and the line carried on. The second printf, with more values than it needed, printed the first and ignored the rest. When a line of output ends in a number you never gave it, count the %s.
The letter is never checked against the value. %d of a string prints a number - the address in memory where the string is - and %s of a number goes to that address and prints whatever it finds, which on this machine is usually a row of graphics characters. Neither is an error to the compiler or to printf. If a line prints an unexpected number, or a burst of blocks, the letter and the value have crossed.
%d in hex is %x to %d in hex is %q. q is not a letter this printf knows. What does it print, and does the octal number still come out?5 out of "Brick has %d letters\n"'s arguments. Predict the shape of the output before you run it.%s has to %.3s has. What is printed for the name?8.1 Print a receipt: three items and their prices in whole pence, one to a line, with the names in a field 12 wide and the prices right-aligned in a field 6 wide.
8.2 Print the numbers 1 to 5 down the screen, each in hexadecimal, octal and decimal, in columns four wide.
8.3 Print the line 50% of 64K is 32K using one printf with three %ds and a %%.
Worked solutions are in Appendix II.
| Symptom | Cause |
|---|---|
| A number you never gave appears at the end of a line | More %s than values. |
| A number where you expected a word, or a row of blocks where you expected a number | %d given a string, or %s given a number. |
undefined symbol naming a library function, at the end of the compile | No #include ?STDIO.LIB?, or it is above the line that calls the function. It goes last. |
printf prints its string, substituting the next value at each %: %d %u %x %o %c %s, with a width, - or 0 in front, and %% for a percent sign. Too few values print rubbish, too many are ignored, and nothing checks the letter against the value. It is built in; the rest of the library is read from STDIO.LIB by the last line of every program.
S9, Numbers - what an int can hold on this machine and what happens past the end of it, unsigned and char, and why there are no fractions in this course.