πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English
Advertisement

πŸ”¦ Lesson 12: Stuck? printf every variable

Turn the time you spend staring at code into time spent seeing variable values. The most practical debugging skill for beginners.

πŸ“– What to learn on this page
βœ… Must-know essentials
  • Not understanding = not seeing the values
  • When in doubt: printf("x=%d\n", x);
  • Drop them inside loops, at function entry/exit
⭐ Read if you have time
  • Tag debug lines with [DEBUG] for grepping later
  • Watch out for output buffering (always \n)
  • Graduate to gdb / lldb / IDE debuggers once fluent

Why printf debugging is the strongest tool β€” 90% of beginners get stuck here

Here's the pattern that traps most beginners:
Staring at the code and trying to run it in their head. Then freezing when they can't figure it out.
πŸ”΄ Wrong approach
Fatal flaw: no human brain can track 5 or 6 variables at once. Add a loop or branch and it all falls apart. "Eyeballing" = giving up.
βœ… Right approach
Let the computer show you the values. That's it.
// Drop printfs everywhere suspicious
printf("[DEBUG] i=%d, sum=%d\n", i, sum);
Ten printfs take 3 minutes. Those 3 minutes buy you the same information as an hour of staring. This is the single biggest gap between beginners and pros.
🎯 Pros do the same thing: senior engineers use IDE debuggers or gdb, but the core activity β€” "watch live variable values" β€” is identical. printf is just the friendliest way in.

Where to drop printfs β€” the three golden patterns

Pattern β‘  Inside loops β€” print every iteration
for (int i = 0; i < 5; i++) {
    sum = sum + arr[i];
    printf("[DEBUG] i=%d, arr[i]=%d, sum=%d\n", i, arr[i], sum);  // ← this
}
Loops are where things go sideways. Printing state each iteration pinpoints exactly which pass broke.
Pattern β‘‘ Function entry and exit
int calc(int a, int b) {
    printf("[DEBUG] calc entry: a=%d, b=%d\n", a, b);
    int result = a * 2 + b;
    printf("[DEBUG] calc exit: result=%d\n", result);
    return result;
}
See what came in and what goes out. Instantly tells you whether the bug is inside the function or in the caller.
Pattern β‘’ Around if branches
printf("[DEBUG] before check: score=%d\n", score);
if (score >= 60) {
    printf("[DEBUG] pass branch\n");
    printf("Pass\n");
} else {
    printf("[DEBUG] fail branch\n");
    printf("Fail\n");
}
99% of "why isn't my branch taken?" bugs are the condition variable being different from what you think. Print it just before the check.
πŸ’‘ When unsure: printf every variable that could be wrong. Overkill is fine β€” you'll delete them later.

Before / After β€” same bug, very different pain

A classic bug: "summing 1 to 10 should give 55, but I get 45." Compare the two debugging approaches.
😩 Before: no printfs
int sum = 0;
for (int i = 1; i < 10; i++) {
    sum = sum + i;
}
printf("Sum = %d\n", sum);
// Output: Sum = 45
// "Wait, shouldn't it be 55?"
// ...stare at code for 10 minutes...
// ...still lost...
Time wasted: 10–60 min
😌 After: add printfs
int sum = 0;
for (int i = 1; i < 10; i++) {
    sum = sum + i;
    printf("[D] i=%d sum=%d\n", i, sum);
}
printf("Sum = %d\n", sum);
// Output:
// [D] i=1 sum=1
// [D] i=2 sum=3
// ...
// [D] i=9 sum=45    ← i stops at 9, not 10!
// Sum = 45
Time wasted: 30 sec β†’ change i<10 to i<=10
The difference: printfs tell you the fact "i maxed out at 9". Without them, the distinction between i < 10 and i <= 10 stays fuzzy in your head, and the bug never gets fixed.

Format specifier cheat sheet

Different types need different format specifiers. Copy-paste from here when in doubt.
TypeSpecifierExampleOutput
int%dprintf("%d", x)42
long%ldprintf("%ld", y)1234567890
float / double%fprintf("%.2f", z)3.14
char%cprintf("%c", c)A
char[] / string%sprintf("%s", s)Hello
pointer/address%pprintf("%p", p)0x7ffee...
hex%xprintf("%x", n)ff
πŸ’‘ Fastest cheat template: paste printf("[D] var1=%d var2=%d\n", var1, var2); everywhere and swap in the variable names. Don't forget \n (see next section).

Pitfalls and cleanup

πŸ”΄ Pitfall β‘  Missing \n delays output
printf buffers its output for efficiency and only flushes on newline \n (line-buffering).
printf("[D] got here");   // ← no \n β†’ nothing on screen yet!
// If the program crashes after this, you'll think
// "we didn't reach here" even though we did.
Fix: always end debug printfs with \n, or call fflush(stdout); right after.
⚠️ Pitfall β‘‘ Wrong specifier prints garbage
double x = 3.14;
printf("%d\n", x);    // ❌ %d is for int; undefined value
Fix: check the table above. Compile with -Wall so the compiler warns you about mismatches.
🧹 Cleanup β€” delete before committing
When the bug is fixed, delete every debug printf. Leaving them behind makes the code hard to read and can flood production output.
πŸ’‘ Make them easy to find: always tag debug prints with [DEBUG] or [D]. Then one grep for [D] finds them all.
πŸš€ Next step: Once you're comfortable with printf, move to Debugging Techniques and gdb practical guide. Being able to pause and inspect variables interactively doubles or triples your speed.
Advertisement

Related Lessons

Intro
printf & scanf
Refresh the specifier basics here.
Loops & arrays
Debugging Techniques
Deeper debugging strategies and common bug types.
Advanced topics
gdb practical guide
The natural next step β€” interactive variable inspection.
← Previous
Comparison & Logical
Next β†’
if Statement