Topic 39 of 100 Input and Output Functions Interactive Tutorial

C Format Specifiers: %d, %f, %s and the Complete Reference

14 mins read Last updated: 22 Aug 2026 31,400 views
C Compiler 0.00%

What is C Format Specifiers? Core Concept & Direct Summary

Core Objective & Problem Statement

What is the complete reference of C format specifiers for integers, floating points, characters, strings, pointers, and fixed-width types?

Direct Answer Summary

If C I/O functions were a language, format specifiers would be its alphabet. Those little two-character codes — %d, %f, %s — appear in nearly every printf() and scanf() you'll ever write, and picking the wrong one is behind a shocking number of beginner bugs.

Here's the good news: there are only about a dozen you actually need to know, and each one follows the same simple logic. This page is the complete reference — every specifier that matters, what it works with, how it behaves differently in printf() versus scanf(), and the mistakes to dodge. Bookmark this one; you'll be coming back to it.

(Suggested image: a colorful "cheat sheet" style graphic of the specifier table — this page's hero image and your most shareable asset in the whole series)


How Does C Format Specifiers Work? In-Depth Mechanics & Architecture

What Is a Format Specifier, Exactly?

A format specifier is a placeholder inside a format string. It starts with % and ends with a type character, and it tells the function two things:

  1. In printf(): "Insert a value here, and format it as this type"
  2. In scanf(): "Expect this type of input, and store it in the matching variable"
main.c
Run Online
printf("I am %d years old and %.1f meters tall.\n", 21, 5.9);
//       ^^ placeholder for int        ^^^ placeholder for float

Each specifier grabs its value from the argument list in order. First % pairs with the first argument, second % with the second, and so on.


The Big Five: Specifiers You'll Use Every Day

Let's go deep on the five specifiers that cover 90% of real-world code.

%d — Signed Integer

The workhorse of C programming. Reads or prints whole numbers:

main.c
Run Online
int age = 21;
int score = -150;
printf("Age: %d\n", age);
// Age: 21printf("Score: %d\n", score);   // Score: -150
int input;
scanf("%d", &input);
// reads an integer

(%i does the same job in printf(). In scanf() it differs slightly — %i auto-detects octal/hex prefixes while %d always assumes decimal — but for everyday use, treat them as twins.)

%f — Floating Point

For decimal numbers:

main.c
Run Online
float price = 99.95f;
printf("Price: %f\n", price);
// Price: 99.950000 (6 decimals by default)printf("Price: %.2f\n", price);   // Price: 99.95
float amount;
scanf("%f", &amount);
// reads a float

The default six decimal places surprises everyone at first. Tame it with precision: %.2f for money, %.1f for measurements.

%s — String

Prints or reads a whole string (character array):

main.c
Run Online
char name[] = "Priya";
printf("Hello, %s!\n", name);
// Hello, Priya!
char input[50];
scanf("%49s", input);
// reads ONE word (stops at space)

Two things to burn into memory: scanf("%s") stops at whitespace (use fgets() for full lines), and always give it a width limit (%49s for a 50-char array) to prevent buffer overflow. And no & — array names already act as addresses.

%c — Single Character

main.c
Run Online
char grade = 'A';
printf("Grade: %c\n", grade);
// Grade: A
char initial;
scanf(" %c", &initial);
// note the space — skips leftover newlines!

That leading space in scanf(" %c") is crucial — without it, %c grabs leftover whitespace from previous input. We covered this buffer behavior in detail in the scanf() tutorial.

%x — Hexadecimal

Prints integers in base-16 — essential for debugging, memory work, and embedded programming:

main.c
Run Online
int value = 255;
printf("%x\n", value);
// ffprintf("%X\n", value);
// FF  (uppercase)printf("%#x\n", value);
// 0xff (with prefix)
scanf("%x", &value);
// reads hex input like "ff" as 255

The Complete Reference Table

Here's the full list, organized by category:

Integers

SpecifierTypeNotes
%dint (signed decimal)The default integer choice
%iintSame as %d in printf; auto-detects base in scanf
%uunsigned intNever prints a minus sign
%ldlongFor bigger numbers
%lldlong longFor really big numbers
%lu / %lluunsigned long / long longBig + unsigned
%hdshortRarely needed in printf
%ooctal (base 8)255 → 377
%x / %Xhexadecimal (base 16)255 → ff / FF

Floating Point

SpecifierTypeNotes
%ffloat/doubleDefault 6 decimals; use %.2f etc.
%e / %Escientific notation31415.9 → 3.141590e+04
%g / %Gshortest of %f or %eDrops trailing zeros — clean output
%lfdouble in scanf onlyIn printf, %f already covers double

Characters & Strings

SpecifierTypeNotes
%ccharSingle character
%sstringStops at whitespace in scanf

Special

SpecifierTypeNotes
%ppointer addressPrints memory address (0x7ffc...)
%zusize_tCorrect type for sizeof results
%%literal percentNo argument needed
%ncharacters written so far⚠️ Avoid — security risk, banned in many codebases

printf vs scanf: Where the Same Specifiers Behave Differently

This is the section most references skip — and where most bugs live. The specifier codes look identical in both functions, but the rules differ in subtle, important ways:

Difference 1: %f vs %lf

main.c
Run Online
double d = 3.14159;
printf("%f", d);
// ✓ correct — printf treats %f as doublescanf("%f", &d);
// ✗ WRONG — corrupts memory!scanf("%lf", &d);
// ✓ correct — scanf needs %lf for double

Why? In printf(), floats get automatically promoted to doubles when passed as arguments, so %f works for both. But scanf() receives addresses, not values — it needs to know the exact size of the variable it's writing into. %f = 4-byte float, %lf = 8-byte double. Mix them up and scanf() writes 8 bytes into a 4-byte home. No error. Just silent corruption.

Difference 2: The & operator

main.c
Run Online
int age;
printf("%d", age);
// value — no &scanf("%d", &age);
// address — & required

printf reads values; scanf writes to addresses. The only exception: strings, which never take & in either function.

Difference 3: Whitespace handling

main.c
Run Online
scanf("%d", &x);
// skips leading whitespace automaticallyscanf("%c", &ch);
// does NOT skip whitespace — reads leftover '\n'!scanf(" %c", &ch);
// the space fixes it

Numbers skip whitespace by default; characters don't. Asymmetry = bugs.

(Suggested image: a two-column comparison graphic — "printf side vs scanf side" listing these three differences)


What Happens When You Use the Wrong Specifier?

Format mismatches cause undefined behavior — C-speak for "anything can happen, and none of it is good":

main.c
Run Online
float pi = 3.14159f;
printf("%d\n", pi);
// prints garbage like 1078530011
int age = 21;
printf("%f\n", age);
// prints 0.000000 or crashes
char letter = 'A';
printf("%s\n", letter);
// likely SEGFAULT — %s expects an address

The %s mismatch is the dangerous one: it treats your value as a memory address and starts reading whatever lives there. The integer/float mixups usually "just" print garbage — the bit pattern of a float reinterpreted as an integer, which is why the numbers look so bizarre.

Protect yourself: compile with warnings enabled:

main.c
Run Online
gcc -Wall -Wformat program.c

With -Wformat, GCC checks your format strings at compile time and catches mismatches before they bite. Turn it on, always.


The %g Trick Nobody Teaches

Tired of 3.140000? %g automatically chooses between %f and %e — whichever is shorter — and strips trailing zeros:

main.c
Run Online
printf("%g\n", 3.140000);
// 3.14printf("%g\n", 1000000.0);
// 1e+06printf("%g\n", 0.00001234);
// 1.234e-05printf("%g\n", 42.0);
// 42

For general-purpose number printing where you don't need fixed decimals, %g gives the cleanest output. Criminally underused.


Width and Precision: The Quick Reminder

Every specifier accepts the full decoration pattern we covered in the formatted I/O tutorial:

main.c
Run Online
printf("%5d\n", 42);
// width: "   42"
printf("%-5d|\n", 42);
// left-align: "42   |"
printf("%05d\n", 42);
// zero-pad: "00042"
printf("%.2f\n", 3.14159);
// precision: "3.14"
printf("%.3s\n", "Hello");
// max chars: "Hel"
printf("%+d\n", 42);
// force sign: "+42"

The pattern is always %[flags][width][.precision][length]type — and it works identically across every specifier in the table.


A Test-Yourself Example

Before checking the output, try to predict what each line prints:

main.c
Run Online
#include <stdio.h>
int main() {
    int n = 255;
    float f = 3.5f;
    char c = 'Z';
    char s[] = "Specification";
    printf("1: %d %o %x\n", n, n, n);
    printf("2: %.1f and %g\n", f, f);
    printf("3: %c is ASCII %d\n", c, c);
    printf("4: %.5s...\n", s);
    printf("5: 100%% complete\n");
    return 0;
}

Output:

main.c
Run Online
1: 255 377 ff
2: 3.5 and 3.5
3: Z is ASCII 90
4: Speci...
5: 100% complete

Line 3 is the one worth a second look: the same variable printed as %c shows the character, while %d shows its ASCII value. One variable, two views — that's the whole idea of format specifiers in a nutshell.


The Ten Commandments of Format Specifiers

  1. Match the specifier to the variable type — always, no exceptions
  2. Double in scanf needs %lf; in printf, %f handles both
  3. & in scanf, never in printf; strings never need & at all
  4. Limit string input: scanf("%49s", arr) for char arr[50]
  5. Space before %c in scanf to skip leftover newlines
  6. %.2f for money, %g for clean general output
  7. %% prints a literal % — no argument needed
  8. %zu for sizeof results — not %d
  9. Compile with -Wall -Wformat so the compiler checks your specifiers
  10. Never use %n — it's a security hole, not a feature

(Suggested image: a styled "Ten Commandments" graphic — this is prime Pinterest/LinkedIn content and reinforces the reference-page identity)


Final Thoughts

Format specifiers look like trivia, but they're really a contract: you tell C exactly what type of data is crossing the boundary between your program and the outside world, and C trusts you completely. Keep your side of the contract — right specifier, right type, right size — and I/O bugs practically vanish.

You don't need to memorize the whole table today. Know the big five cold (%d, %f, %s, %c, %x), remember the %lf scanf rule, and bookmark this page for everything else. That's how everyone actually works — including people who've written C for decades.

Runnable C Code Example & Practical Implementation

main.c
Run Online
#include <stdio.h>
int main() {
    int integer = 100;
    double pi = 3.14159265;
    char letter = 'C';
    char title[] = "Tutorial";
    void *addr = &integer;
    size_t size = sizeof(double);
    printf("Integer:  %d | Unsigned: %u\
", integer,(unsigned)integer);
    printf("Float:    %.4f | Scientific: %.2e\
", pi, pi);
    printf("Char:     %c | String: %s\
", letter, title);
    printf("Pointer:  %p | size_t: %zu bytes\
", addr, size);
    return 0;
}

Key Takeaways, Best Practices & Memory Safety Rules

Best Practice & Key Takeaways
Always cast pointers to (void*) when printing with %p (printf("%p", (void*)ptr)) to comply with ANSI/ISO C standards.

Frequently Asked Questions & Technical Interview Insights

Technical Interview & Viva Questions

Frequently asked C programming interview questions
Q

Q: Why is `%lf` required in `scanf` for double, but `%f` is used in `printf` for double?

A: In `printf`, variadic argument promotion rules automatically promote `float` arguments to `double`, so `%f` handles `double`. But in `scanf`, pointers are passed (`float*` vs `double*`), which have distinct memory sizes (4 bytes vs 8 bytes), requiring `%lf` for `double*`.
Q

Q: What specifier portably formats a `size_t` variable?

A: `%zu` (introduced in C99).

Related Tools, Tutorials & Authoritative References

Recommended Interactive Tools

Authoritative Standards & Citations