printf Is a Small Language

You have used printf since the first lesson, but so far only its alphabet. The format string is actually a compact language for laying out output: every conversion specification has the shape %[flags][width][.precision]specifier, and each part is under your control. Exams ask you to predict formatted output character by character; this platform grades exercises byte by byte; both make fluency worth an hour.

The Specifiers, Collected

The conversion specifier picks the type and rendering. The working set, most already familiar:

Specifier Prints Notes
%d int, decimal
%i int, decimal identical to %d here; see the note below
%u unsigned int
%ld, %lu long, unsigned long the l is a length prefix
%f double, fixed point six decimals by default
%e double, scientific 2.500000e+03
%g double, whichever of the two is shorter six significant digits, trailing zeros dropped
%c one character
%s a string fully explored in chapter 8
%o, %x unsigned int in octal, hex chapter 2's other bases
%% a literal %

%i and %d are the same conversion for output, and exam papers print both. The difference exists on the input side: scanf("%i") inspects the text and reads 0x1f as hexadecimal and 017 as octal, while scanf("%d") always reads decimal. For printf, pick one and be consistent.

%g is the specifier for numbers whose magnitude you do not know in advance. It formats like %e when the exponent is large or very negative and like %f otherwise, and it drops trailing zeros either way:

[0.500000] [5.000000e-01] [0.5]
[100] [1.25e+06] [1.25e-05]
[3.14] [100.000]

One value, three renderings on the first line, and %g is the only one that prints 0.5 without decoration. On the second line it stays fixed-point for 100 and switches to exponential for a million and for a hundred-thousandth. Note what precision means here: %.3g gave 3.14, three significant digits rather than three decimals. The %#g on the end is a flag from the next section: it forces the trailing zeros back.

The rule underneath the table: the specifier must match the argument's type. printf cannot check; it trusts the format string and reads the raw bytes accordingly, so a mismatch such as %d handed a double is undefined behaviour, not a formatting quirk. The compiler's format warnings catch most mismatches; treat every one as an error.

Width: Claiming Columns

A number between the % and the specifier sets the minimum field width. Output shorter than the field is padded with spaces on the left; output longer simply overflows, width never truncates.

The brackets reveal the padding: three leading spaces for 42, a snug fit for 12345, and an overflowing six digits for 123456. Width is what lines up columns in tables, which is precisely what exams use it for.

Two flags modify the fill. - left-justifies, padding on the right instead: %-5d. 0 pads with zeros rather than spaces: %05d prints 00042, and it only makes sense for numbers.

Precision: Controlling Digits

A dot and a number set precision, whose meaning depends on the specifier. For %f it is digits after the decimal point, rounding the value: %.2f gives money, %.0f a whole number. You have used this since chapter 1; now it has its name.

The second line combines width and precision: ten columns total, two after the point, space-padded to [ 1234.57]. The third left-justifies inside the same ten columns; the fourth zero-fills. Read %10.2f inside out, precision shapes the number, width places it, and any combination becomes predictable.

On a String, Precision Truncates

Precision means something different for %s: it is the maximum number of characters to print. Where width pads a short string, precision cuts a long one, and that is the only part of the format language that discards your data on purpose.

[New Delhi 110001]
[New D]
[          New Delhi ]
[New Delhi           ]

%.5s kept five characters. %20.10s took the first ten, New Delhi with its trailing space, and right-justified them in twenty columns; adding - moved the padding to the other side. This is how a report keeps a ragged column of names inside a fixed layout, and it is worth knowing that a truncated field looks like ordinary output: nothing marks the cut.

More Flags: + and

Two flags remain beyond the - and 0 from the width section, and both change what gets printed rather than where it sits.

+ forces a sign onto a signed number, so positive values print with + instead of nothing. A space in the same position is the quieter variant: it reserves a column for the sign that only a negative number fills, which keeps a column of mixed-sign numbers aligned.

# asks for the alternate form of a conversion. With %o it guarantees the leading 0, with %x the leading 0x, so the output is unambiguous about its base. With %e, %f, and %g it forces the decimal point to appear even when nothing follows it, and it stops %g from dropping trailing zeros.

[42] [+42] [-42] [ 42]
[100] [0100] [ff] [0xff]
[2] [2.]

Line one: the same 42 plain, with +, then a negative value showing that + never overrides a real minus, then the space flag holding a column open. Line two: 64 in octal is 100, which is indistinguishable from one hundred until %#o writes 0100; %#x does the same job with 0x. Line three is the odd one worth seeing once, a %.0f asking for no decimals at all, where # leaves a trailing point behind with nothing after it.

Building a Table

The pieces compose into the classic mark-sheet layout:

Left-justified names in ten columns, right-justified prices in eight with two decimals: the columns align without a single hand-counted space. This is the pattern behind every "print a neat table" exam question, and behind real report output to this day.

What printf Returns

Like scanf, printf has a return value: the number of characters it wrote, or a negative value on output failure. Everyday code rarely consults it, and this course's programs do not; it is mentioned here because the symmetry matters in the next lesson, where scanf's return value is not optional but essential.

Key Takeaways

  • A conversion spec is %[flags][width][.precision]specifier; each part is independent.
  • Specifier and argument type must match; a mismatch is undefined behaviour, and format warnings are errors in practice.
  • Width is a minimum field, space-padded left; - left-justifies, 0 zero-fills; overflow never truncates.
  • Precision after %f sets decimals and rounds: %.2f; combined %10.2f reads precision-then-placement.
  • On %s precision is a maximum instead: %.5s truncates, and nothing in the output marks the cut.
  • %i prints exactly like %d; the two differ only in scanf, where %i detects hex and octal prefixes.
  • %g picks the shorter of %f and %e and drops trailing zeros, and its precision counts significant digits, not decimals.
  • + forces a sign and a space reserves its column; # gives %o and %x their 0 and 0x prefixes and keeps the decimal point on %e, %f, and %g.
  • %-10s beside %8.2f builds aligned tables with no counted spaces.
  • printf returns characters written; scanf's return value, next lesson, is the one you must not ignore.