Part of C in 100s

C in 100 Seconds: Format Specifiers | Episode 3

Celest KimCelest Kim

Video: C in 100 Seconds: Same Data Different Output — Format Specifiers | Episode 3 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

C Format Specifiers: Same Data, Different Output

%d decimal, %x hex, %f float, %.2f precision, %s string, %p pointer, %10d right-padded, %-10d left-padded. The printf format mini-language.

printf's power is its format string. The same value can be printed as decimal, hex, padded, scientific notation, or address — depending on which format specifier you use.

The full example

#include <stdio.h>

int main() {
  int n = 42;
  float pi = 3.14159;
  char name[] = "Alice";

  printf("%d\n", n);          // 42
  printf("%x\n", n);          // 2a
  printf("%f\n", pi);         // 3.141590
  printf("%.2f\n", pi);       // 3.14
  printf("%s\n", name);       // Alice
  printf("%p\n", &n);         // 0x7ffe... (address)
  printf("[%10d]\n", n);      // [        42]
  printf("[%-10d]\n", n);     // [42        ]

  return 0;
}

Eight different format specifiers, each rendering the same data differently.

Integer formats

printf("%d\n", 42);     // decimal:   42
printf("%i\n", 42);     // also decimal: 42 (synonym for %d)
printf("%x\n", 42);     // lowercase hex: 2a
printf("%X\n", 42);     // uppercase hex: 2A
printf("%o\n", 42);     // octal: 52
printf("%u\n", 42u);    // unsigned: 42
printf("%b\n", 42);     // binary (C23+): 101010

For longer integers, prefix the specifier:

printf("%ld\n", 9999999999L);    // long
printf("%lld\n", 9999999999LL);  // long long
printf("%zu\n", sizeof(int));    // size_t (return of sizeof)

Float formats

printf("%f\n", 3.14159);     // 3.141590 (default 6 decimals)
printf("%.2f\n", 3.14159);   // 3.14 (2 decimals)
printf("%.0f\n", 3.14159);   // 3 (rounded)
printf("%e\n", 314159.0);    // 3.141590e+05 (scientific)
printf("%.3e\n", 314159.0);  // 3.142e+05
printf("%g\n", 3.14);        // 3.14 (shortest of %f or %e)

%f is the most common. %.Nf for N decimal places. %e for scientific notation. %g picks whichever is shorter.

Strings and characters

char name[] = "Alice";
printf("%s\n", name);        // Alice
printf("%c\n", name[0]);     // A
printf("%d\n", name[0]);     // 65 (ASCII for 'A')

%s for null-terminated strings. %c for a single char. %d on a char prints its ASCII value.

Pointers

int n = 42;
printf("%p\n", (void*)&n);   // 0x7ffe... (memory address)

%p prints a pointer's address as hex. Cast to void* for portability — printf expects void*, and although most compilers accept int* directly, the standard says cast.

Width and padding

printf("[%10d]\n", 42);      // [        42] — right-aligned, width 10
printf("[%-10d]\n", 42);     // [42        ] — left-aligned, width 10
printf("[%010d]\n", 42);     // [0000000042] — zero-padded to width 10
printf("[%5.2f]\n", 3.14);   // [ 3.14] — width 5, 2 decimals

Widths control the minimum output width. If the value is wider, it overflows (no truncation).

- flag: left-align. 0 flag: zero-pad (only useful for numbers, right-align). + flag: always show sign.

printf("%+d\n", 42);    // +42
printf("%+d\n", -42);   // -42

Precision

For %f, precision = decimals. For %s, it's max string length:

printf("[%.3s]\n", "Hello");   // [Hel] (truncated to 3 chars)
printf("[%5.3s]\n", "Hello");  // [  Hel] (width 5, max 3 chars)

For %d, precision = minimum digits (zero-padded):

printf("[%.5d]\n", 42);   // [00042]

Variable width and precision

* lets you pass width or precision as an argument:

int width = 10;
int n = 42;
printf("[%*d]\n", width, n);   // [        42]

int prec = 4;
double pi = 3.14159;
printf("%.*f\n", prec, pi);    // 3.1416

Useful when the formatting depends on runtime data — column widths in a table, etc.

Common mistakes

Wrong specifier for the type. printf("%d", 3.14); prints garbage — the compiler reinterprets the double's bytes as an int. Match types.

Forgetting the type promotion. printf("%f", 3.14f); works because float promotes to double when passed. So %f covers both.

Using %s on a non-string. printf("%s", 42); segfaults — %s expects a char*, gets the int 42 as a "pointer," tries to dereference. Crash.

Forgetting \n. Output buffers until newline (or flush, or program exit). Without \n, you might not see output before a crash.

Using %lf for double. Some old C texts say %lf. In modern C (C99+), %f works for both float and double in printf. (scanf is different — there %lf is required for double.)

%X vs %x. Different! Uppercase prints 2A; lowercase 2a.

Why this matters

printf is the workhorse of C debugging. Knowing the format specifiers is half of "is my program doing what I think." For tables, padding makes columns align. For low-level debugging, %x and %p reveal what's actually in memory.

What's next

Episode 4: arithmetic operators. + - * / % and the surprising integer-division truncation: 7 / 2 is 3, not 3.5.

Recap

Integer: %d decimal, %x/%X hex, %o octal, %u unsigned. Float: %f (6 decimals default), %.Nf N decimals, %e scientific. String: %s. Char: %c (or %d for ASCII value). Pointer: %p (cast to void*). Width: %10d (right-align), %-10d (left-align), %010d (zero-pad). Precision: %.2f (decimals for floats; max length for strings). Variable: %*d and %.*f. Mismatch types and you get garbage or a crash.

Next episode: arithmetic and integer division.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.