Output with printf
Print exactly what you wantprintf prints text and turns values into text through format specifiers such as %d and %.2f.
In the lab below you build a specifier piece by piece — flags, width, precision — and watch printf build the output
character by character, see what goes wrong when a specifier does not match its value, and meet puts and putchar.
- Pattern%[flags][width][.precision]type
- %8.2f8 wide, 2 decimals
- %%prints one %
Ready
Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode
The ideas behind the lab
Exactly what you watched happen, in plain steps.
The parts of a conversion specification
printf copies its format string to the screen. Each % starts a conversion specification that is replaced by the next argument. Between % and the conversion letter you can add flags, a minimum width and a precision — in exactly this order.
% [flags] [width] [.precision] conversion %d 42 → 42 %5d 42 → 42 %-5d| 42 → 42 | %05d 42 → 00042 %+d 42 → +42 %.2f 3.14159 → 3.14 %8.2f 3.14159 → 3.14 %e 12345.678 → 1.234568e+04
| Specifier | Argument type | Prints | Example | Output |
|---|---|---|---|---|
| %d %i | int | signed decimal | printf("%d", -42) | -42 |
| %u | unsigned int | unsigned decimal | printf("%u", 42u) | 42 |
| %o | unsigned int | octal | printf("%o", 8) | 10 |
| %x %X | unsigned int | hexadecimal | printf("%x %X", 255, 255) | ff FF |
| %c | int (a char) | one character | printf("%c", 'A') | A |
| %s | string (char *) | characters up to \0 | printf("%s", "Hi") | Hi |
| %f | double (float too) | fixed point, 6 decimals | printf("%f", 2.5) | 2.500000 |
| %e %E | double | scientific notation | printf("%e", 1234.5) | 1.234500e+03 |
| %g %G | double | shorter of %f / %e | printf("%g", 0.0001) | 0.0001 |
| %ld %lld | long / long long | signed decimal | printf("%lld", 10000000000LL) | 10000000000 |
| %lu %llu | unsigned long (long) | unsigned decimal | printf("%lu", 42UL) | 42 |
| %zu | size_t (from sizeof) | unsigned decimal | printf("%zu", sizeof(int)) | 4 |
| %p | pointer | an address | printf("%p", (void *) &x) | 0x7ffe… |
| %% | — | a percent sign | printf("100%%") | 100% |
Width and precision
- Width is a MINIMUM: %6d prints 42 as " 42" (right-aligned). Longer values are never cut.
- For %f / %e precision is the number of decimals: %.2f rounds 3.14159 to 3.14.
- For %g precision is the number of significant digits.
- For %d precision is the minimum number of digits: %.4d prints 0042.
- For %s precision is the maximum number of characters: %.3s prints Hel.
The flags
- - left-aligns inside the width: "%-6d|" prints "42 |".
- + always prints a sign: %+d prints +42.
- space puts a space where a + would be: % d prints " 42".
- 0 pads numbers with zeros instead of spaces: %05d prints 00042 (ignored with -).
- # alternate form: %#x prints 0xff, %#o prints 010, %#.0f keeps the point.
Rules that prevent bugs
- Every % needs exactly one argument of the matching type, in the same order.
- printf does not convert types: %f with an int (or %d with a double) prints garbage.
- Compile with -Wall: gcc then checks the format string against the arguments.
- To print a % sign write %%.
- printf returns the number of characters it printed.
A neat bill with printf
Every line, explained.
#include <stdio.h>
int main(void)
{
int qty = 3;
double price = 49.5;
printf("%-10s%5s%10s\n", "Item", "Qty", "Amount");
%-10s left-aligns "Item" in 10 columns; %5s and %10s right-align the other headings.
printf("%-10s%5d%10.2f\n", "Notebook", qty, qty * price);
The same widths line the numbers up under their headings: Notebook, then 3 in the Qty column, then 148.50 in the Amount column.
printf("%-10s%5s%10.2f\n", "GST 18%", "", qty * price * 0.18);
"GST 18%" is a %s ARGUMENT, so its % is printed as it is. 26.73 lands in the Amount column.
printf("%+d%% change\n", 12);
%+d shows the sign, %% prints a percent sign: +12% change
printf("Code: %05d Hex: %#x\n", 42, 255);
Zero padding and the 0x prefix: Code: 00042 Hex: 0xff
return 0;
}
Spot the bug
Mistakes almost every beginner makes — and how to fix them.
A specifier that does not match
double avg = 84.5;
printf("%d", avg); // garbage
double avg = 84.5;
printf("%.1f", avg); // 84.5
printf trusts the format string. %d with a double prints a meaningless number — gcc -Wall warns about it.
Fewer arguments than specifiers
printf("%d + %d = %d\n", a, b);
printf("%d + %d = %d\n", a, b, a + b);
The third %d reads whatever is in memory. Count the % signs and the arguments.
Printing a percent sign
printf("Score: 95%\n");
printf("Score: 95%%\n");
A single % starts a conversion (%\n is not valid). Write %% for one % sign.
Printing a float with %d
float r = 2.5f;
printf("%d", r);
float r = 2.5f;
printf("%f", r); // %lf also works
A float passed to printf becomes a double, so %f prints both float and double. (In scanf it is different: %f for float, %lf for double.)
Expecting width to cut text
printf("%3d", 12345); // expects 345
printf("%3d", 12345); // prints 12345
Width is only a minimum. To cut a string use precision: %.3s.
Check your understanding
Answer each question — you will see the explanation straight away.
What does printf("[%5d]", 42); print?
Width 5, right-aligned: three spaces and 42.
What does printf("%.2f", 2.678); print?
Precision .2 rounds to two decimals: 2.678 → 2.68.
Which specifier prints 255 as ff?
%x prints hexadecimal with small letters; %X with capitals (FF).
How do you print 100% with printf?
%% is replaced by a single %.
What does printf("[%-4d]", 7); print?
The - flag left-aligns: 7 followed by three spaces.
Output with printf quiz
Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.