Global and static objects are initialized with the default value for their type (0 for integers, 0.0 for floats, NULL for pointers etc.)
No other uninitialized value can be relied upon.
Here is a quick explanation why:
Most implementations have all local variables on a stack. This stack grows when you declare local variables or when you call a function, and shrinks when you do the reverse action - exit the scope of a variable or return from a function.
Now let's see a program:
void good()
{
int p = 2;
printf("%d", p);
}
void notgood()
{
int p;
printf("%d", p);
}
int main()
{
good();
notgood();
notgood();
}
Here is the stack at the beginning of the program (stack grows downwards). The stack pointer always points to the top element (represented by an arrow):
|---------------------|
|main's return address| <-- stack pointer
Next is immediately after good()
gets called:
|---------------------|
|main's return address|
|good's return address| <-- stack pointer
Next we declare p and initialize it with 2:
|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) | <-- stack pointer
After that, we have the call to printf:
|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) |
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# | <-- stack pointer
When printf
returns, the return address and parameters are popped of the stack, but they are not erased from memory. That would be inefficient. We simply decrease the stack pointer.
|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) | <-- stack pointer
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# |
Next, our good()
function returns to main:
|---------------------|
|main's return address| <-- stack pointer
|good's return address|
|value 2 (variable p) |
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# |
Call notgood
. Whatever trash is on the stack gets overwritten:
|---------------------|
|main's return address|
|notgood's return addr| <-- stack pointer
|value 2 (variable p) |
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# |
Declare the variable (allocate the space), but we don't initialize. Hence, the old garbage value is still there:
|---------------------|
|main's return address|
|notgood's return addr|
|value 2 (variable p) | <-- stack pointer
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# |
Next, we call printf again. Please note that it's return address actually changes, so the old trash is overwritten on the stack:
|---------------------|
|main's return address|
|notgood's return addr|
|value 2 (variable p) |
|value 2 (parameter) |
|format string address|
|printf's return addr |
|#printf's frame# | <-- stack pointer
So, as you can see, if you don't initialize variables, they take the value of whatever there was on the stack.
Be aware that the program may not work, as the compiler could optimize some function calls out.