22 Output
What to know first
Looking back
Chapter 10 said that printing terminals naturally worked by line buffering — waiting until a line’s worth had gathered before striking the paper. Does today’s printf still carry that habit?
A. It does — when standard output leads to a terminal, output usually gathers in an internal store (a buffer) until it meets a \n and then flows out a line at a time. In chapter 19′s demonstration, the characters of printf("Hello, ") appearing on screen only when the second call’s \n arrived was exactly this. Half a century after the circumstances of the paper typewriter disappeared, its rhythm lives on as software’s default — the commotion this habit causes in practice (“my output isn’t showing!”) and how to govern it are met when we take up input and output properly.
The need for this chapter, and its context
printf uses all three preceding chapters at once — it happens inside a statement (19), with constants as material (20), in the shape of a call (21). So when this chapter ends, hello world has only two lines of debt left.By the end of this chapter
printf faced head on — the frame called a format string, the blanks %d and %s, and matching the format against the materials. By the end of this chapter Part IV’s goal is achieved: one piece of hello world becomes, apart from two lines still on credit, all known sentences.The questions this chapter answers
- Why is what was printed sometimes not visible at once — the program seems to hang and then everything pours out together?
- Why did the
stderrline come out at the top of the listing’s output?
22.1 printf — output with a format
The name printf is short for print formatted — “print it as the format says.” The first material is the format string, and the remaining materials are the values to be fitted into the format’s blanks:
printf(format string, value1, value2, ...)The format string is a frame. Ordinary characters flow straight out, and only the places beginning with % — a conversion specification — are treated as blanks, into which the following value is fitted, turned into characters. This book’s minimal set is three:
%d— turns an integer value into decimal characters and fits it in (decimal).%s— fits a string in as it is (string).%%— not a blank; used when you want to print the percent character itself.
Here is the demonstration. Two values go into two blanks in order:
examples-en/ch22/fmt.c
#include <stdio.h>
int main(void)
{
printf("The answer to %s is %d.\n", "life, the universe and everything", 6 * 7);
return 0;
}
Output
The answer to life, the universe and everything is 42.
A string literal went into the %s slot and the value of the expression 6 * 7 into the %d slot, completing the sentence. The format is the frame, the materials go in order — that is the whole of reading printf. (Conversion specifications have many side branches such as field widths, but they are introduced one at a time as needed; the full list is reference material in the appendix.)
Q. Why is what was printed sometimes not visible at once — the program seems to hang and then everything pours out together?
A. Because of the buffering seen in the looking-back exchange. When output goes to a terminal it flows by lines, but when it goes to a file or another program (chapter 10′s redirection) it gathers until a larger lump has filled and then goes out all at once — a performance trick that reduces round trips. So a program printing progress, once joined to a pipe, appears to “hang and then print in a rush.” When it must go out at once you can order the store emptied with fflush(stdout), and when a program exits normally what remains is flushed automatically — but if it dies suddenly the contents of that store vanish, which is the common reason the log just before an accident is missing.
22.2 Kinds of blank — a slot per kind of value
%d and %s carry you a fair way, but each kind of value has its own blank. Learn the common ones first.
examples-en/ch22/convert.c
/* The common conversion specifications, and lining things up with width and
precision. Part IV has not met variables yet, so every argument here is a
constant or an expression. */
#include <stdio.h>
int main(void)
{
puts("[a different blank for each kind of value]");
printf(" integer %%d -> %d\n", 42);
printf(" real %%f -> %f\n", 3.14);
printf(" character %%c -> %c\n", 'A');
printf(" string %%s -> %s\n", "hello");
printf(" hex %%x -> %x\n", 255);
printf(" percent %%%% -> %%\n");
puts("\n[width — reserve the columns and things line up]");
printf(" |%d|%d|\n", 7, 1234);
printf(" |%6d|%6d| <- six columns, right aligned\n", 7, 1234);
printf(" |%-6d|%-6d| <- a minus means left aligned\n", 7, 1234);
printf(" |%06d| <- a 0 fills the blanks with zeros\n", 42);
puts("\n[precision — how many places after the point]");
printf(" %%f -> %f <- six places by default\n", 2.0 / 3.0);
printf(" %%.2f -> %.2f <- rounded to two\n", 2.0 / 3.0);
printf(" %%8.2f -> |%8.2f| <- width and precision together\n", 2.0 / 3.0);
printf(" %%.3s -> %.3s <- on a string: 'how many characters'\n", "abcdef");
puts("\n[making a table]");
printf(" %-8s %6s\n", "item", "count");
printf(" %-8s %6d\n", "apple", 3);
printf(" %-8s %6d\n", "banana", 12);
printf(" %-8s %6.1f\n", "weight", 4.25);
puts("\n[but width counts BYTES — Hangul does not line up]");
printf(" %-8s %6d\n", "사과", 3);
printf(" %-8s %6d\n", "바나나", 12);
puts(" one Hangul syllable is 3 bytes in UTF-8, so eight columns");
puts(" are not eight characters.");
puts("\n[the other windows for output]");
puts(" puts prints one string and the newline for you");
putchar(' '); putchar(' '); putchar('p'); putchar('c'); putchar('\n');
fprintf(stdout, " fprintf(stdout, ...) is the same as printf\n");
fprintf(stderr, " fprintf(stderr, ...) goes out on the error band\n");
return 0;
}
Output
fprintf(stderr, ...) goes out on the error band
[a different blank for each kind of value]
integer %d -> 42
real %f -> 3.140000
character %c -> A
string %s -> hello
hex %x -> ff
percent %% -> %
[width — reserve the columns and things line up]
|7|1234|
| 7| 1234| <- six columns, right aligned
|7 |1234 | <- a minus means left aligned
|000042| <- a 0 fills the blanks with zeros
[precision — how many places after the point]
%f -> 0.666667 <- six places by default
%.2f -> 0.67 <- rounded to two
%8.2f -> | 0.67| <- width and precision together
%.3s -> abc <- on a string: 'how many characters'
[making a table]
item count
apple 3
banana 12
weight 4.2
[but width counts BYTES — Hangul does not line up]
사과 3
바나나 12
one Hangul syllable is 3 bytes in UTF-8, so eight columns
are not eight characters.
[the other windows for output]
puts prints one string and the newline for you
pc
fprintf(stdout, ...) is the same as printf
| Blank | What it takes | Where the name comes from |
|---|---|---|
%d | an integer, in decimal | decimal |
%f | a real number in point notation — six places by default | floating |
%c | one character | character |
%s | a string | string |
%x | an integer in hexadecimal — %X for capitals | hexadecimal |
%% | not a blank but a percent sign itself | — |
Table 22.1
Integer, real, character, string — chapter 20 taught four ways of writing a constant, and here is one blank for each of the four. The remaining conversions (unsigned integers, pointers, sizes) arrive with the types that need them, and the full list is in appendix B.
22.3 Lining things up — width and precision
A blank can also say how wide and how precise. It pays off immediately when making a table.
| Written | Meaning | Result |
|---|---|---|
%6d | reserve six columns, right aligned | |␣␣␣␣␣7| |
%-6d | a minus means left aligned | |7␣␣␣␣␣| |
%06d | fill the blanks with zeros | |000042| |
%.2f | two places after the point — it rounds | 0.67 |
%8.2f | width and precision together | |␣␣␣␣0.67| |
%.3s | on a string it means how many characters | abc |
Table 22.2
Remember the reading order and it never confuses: after % come alignment (-, 0) → width → dot and precision → the conversion letter.
A common misconception. “Give it a width of eight and eight characters fit”
It counts bytes. The listing’s last table shows it — the English table is neat and the Hangul one is not. One Hangul syllable is three bytes in UTF-8 (chapter 9), so the eight columns %-8s reserves hold barely two Hangul characters.
Lining up a Hangul table in a terminal means counting characters and padding by hand, and even that raises another question: how many cells does one character occupy on screen (East Asian characters take two)? Chapter 72 meets this problem again.
22.4 Output has more than one window
Besides printf there are three more, each for a different place.
| Function | What it does | When |
|---|---|---|
printf | prints by a format | when values are slotted in |
puts | prints one string and the newline | a fixed line. Faster and safer than printf |
putchar | one character | when building output a character at a time |
fprintf | prints to a chosen stream | when it must not go to standard output |
Table 22.3
The last matters. A program has two bands going out (chapter 10) — standard output (stdout), where results flow, and standard error (stderr), where error messages flow.
printf("result: %d\n", 42); /* standard output */
fprintf(stderr, "cannot open the file\n"); /* standard error */They are separated because of redirection (chapter 10). Send the results to a file and the error messages must still reach the screen; pipe the results to the next program and error messages must not be mixed in. “Results to standard output, words for a human to standard error” is the discipline of a program that behaves like a tool (the same grain as chapter 53′s exit status).
Q. Why did the stderr line come out at the top of the listing’s output?
A. Because standard error is not buffered. Standard output, as the recall showed, gathers in a warehouse before flowing; standard error sends at once — an error message must survive even if the program dies a moment later.
So when both bands are captured into one file (as this book’s listing verification does) the order they came out in can differ from the order they were written in. That is design, not accident, and worth knowing when reading logs.
22.5 Matching — the contract between format and materials
A format string is a contract. The number and kinds of blanks promise the number and kinds of materials that must follow. Write two %d and two integer values must follow; a %s slot must receive a string.
Break the promise — put a string in a %d slot, or supply too few materials — and it is outside the contract: undefined behaviour (the concept met in chapter 7 appears in a function’s manual too). Fortunately modern compilers catch most of these mismatches through the warnings switched on in chapter 17 (-Wall) — printf’s format checking is among the oldest and most dependable of the warning features.
In practice. When a format string became a weapon — the format string vulnerability
This matching contract appears in the history of security too. Wanting to print text a user typed, some programs put the input into the format string slot, as inprintf(user_input). If the input is ordinary text there is no problem, but when a malicious user sends in % signs — printf interprets them as blanks and starts rummaging through memory looking for materials that do not exist. Attacks that peeked into or wrecked a program’s insides by this principle were epidemic in the 2000s and earned their own name, the “format string vulnerability.” The correct code is printf("%s", user_input) — putting the input in as a material, not as the format. Do not mix data into the frame — this principle is a refrain of security in general.A common misconception. “printf is the function that prints to the screen”
Practically it is mostly true, but strictly it is not —printf is the function that lets characters flow onto the standard output stream (chapters 10 and 15). If the end of that band is a terminal it appears on screen; turn the band towards a file with the operating system’s facility (redirection) and the same program writes to a file. A program does not know where its output goes, and does not need to — that indifference is the power of the stream design. It is entirely thanks to this that one program serves, without being changed, for the screen, for a file, and as another program’s input.22.6 Closing Part IV — rereading hello world
Time to keep the promise. Here are chapter 15′s six lines again.
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}Reading it: #include is a preprocessing directive pasting in the roster of the stdio toolbox (chapters 16 and 19), main’s block binds a list of statements (chapter 19), the expression statement of the printf call lets text flow onto standard output with a string literal as material (chapters 20, 21 and 22), and \n breaks the line (chapter 10). What was an incantation on first meeting has become almost entirely known sentences.
Exactly two lines remain on credit — the int and (void) of int main(void), and return 0. Both are unravelled only once you know the world of declarations, that is, how to make names and types yourself, and that is the next part. At the end of it the debt is settled in full — and then, at last, we get to take input too.