The Problem: Your printf Shows a Random Number
You declare a variable, print it before assigning anything, and get a number that looks like it came from nowhere:
#include <stdio.h>
int main(void) {
int total;
printf("Total: %d\n", total);
return 0;
}
Run it and you might see Total: 32764. Run it again and it might say Total: -1073745472. Change nothing, rebuild, and it can be a third number.
That is a garbage value: whatever happened to be sitting in that memory before your program claimed it. It is not random in the sense of being generated — it is leftover data from something that ran earlier.
Why It Happens: Memory Is Not Cleaned for You
When you write int total;, C reserves four bytes for it. That is all it does. It does not write zero into them.
Those four bytes already have a value — whatever the previous owner left there. Your program, the C runtime, or the operating system used that memory a moment ago. C hands it to you as-is because zeroing every variable would cost time, and C is built to be fast.
So the number you see is real data, just not yours. That is why it changes between runs: the memory layout differs each time.
The Fix: Initialise When You Declare
Give the variable a value at the moment you create it:
int total = 0;
double average = 0.0;
char grade = '\0';
Now printf("Total: %d\n", total); prints Total: 0 every time, on every machine.
This is not optional politeness. It is the difference between a program that works and one that works on your computer today.
The Trap That Bites Harder: Uninitialised Arrays
A single uninitialised int prints a wrong number. An uninitialised char array can print a whole page of junk, or crash:
char name[20];
printf("Name: %s\n", name); // prints junk until it finds a 0 byte
%s keeps printing characters until it hits a null terminator ('\0'). Your array has no terminator, so printf walks straight past the end of it and keeps going through whatever memory follows. Sometimes you get gibberish. Sometimes you get a segmentation fault.
Fix it the same way:
char name[20] = ""; // first byte is '\0', so %s prints nothing
char buffer[100] = {0}; // every byte zeroed
= {0} is the idiom worth remembering. It zeroes the entire array, not just the first element.
Why the Compiler Sometimes Warns and Sometimes Doesn’t
With warnings on, GCC and Clang usually catch this:
gcc -Wall -Wextra -o program program.c
You get:
warning: 'total' is used uninitialized [-Wuninitialized]
But the warning is not guaranteed. If the variable is passed to another function, or its use is behind a condition the compiler cannot follow, the warning disappears and the bug ships.
That is why -Wall -Wextra is a habit, not a guarantee. It catches the obvious cases. It will not catch all of them.
Finding It When the Compiler Stays Quiet
Two tools find what the compiler misses:
Valgrind (Linux, macOS) reports reads of uninitialised memory at runtime:
valgrind --track-origins=yes ./program
You get a line naming the exact variable and the line number where it was read before being written.
AddressSanitizer works on Windows too, through GCC or Clang:
gcc -fsanitize=address -g -o program program.c
./program
It is faster than Valgrind and catches out-of-bounds reads as well — which is what the uninitialised char array above actually is.
The Related Bug You Will Meet Next
Garbage values and scanf problems tend to appear in the same program, because both come from not controlling what is in memory. If your scanf is skipping input or looping forever, that is a separate cause with a separate fix:
Why your C program skips the second scanf — the leftover newline in the input buffer
Why your C program loops forever when you type a letter —
scanffailing and never consuming the bad inputWhy your C program crashes when you forget & in scanf — writing to an address that isn’t yours
The Rule, Stated Once
In C, a variable has a value the moment it exists — you just did not choose it.
Initialise every variable when you declare it. Use = {0} for arrays. Compile with -Wall -Wextra. Reach for Valgrind or AddressSanitizer when the compiler says nothing but the output looks wrong.
The habit costs one line per variable and removes an entire class of bug that is otherwise almost impossible to reproduce.
Originally published on my blog. I post new C, Python and AI-agent guides there regularly.
Top comments (0)