A different example may make this a bit more clear.
Assume you have the following function:
void foo( void )
{
int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}
We create a variable a
with an initial value of 1
, print out its address and value, and then increment it. This variable is not visible outside the scope of the foo
function.
The variable a
has auto
storage duration; its lifetime is limited to the lifetime of the foo
function. A new instance of a
will be created and initialized every time foo
is entered, and that instance is destroyed every time foo
exits1.
Now we add the static
keyword to the declaration:
void foo( void )
{
static int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}
a
now has static
storage duration; a single instance of a
is created and initialized once at program startup, and that instance is maintained until the program exits. a
is still only visible by name within the scope of foo
, but its lifetime extends beyond the lifetime of the foo
function.
In both cases, the value of a
is updated in exactly the same way; we update the contents of the memory location that a
corresponds to. The only difference is how the contents of that memory location are maintained over the lifetime of the program.
To drive this home, take the following code:
#include <stdio.h>
void foo( void )
{
int a = 1;
printf( "&a = %p, a = %d\n", (void *) &a, a );
a++;
}
void bar( void )
{
int b = 2;
foo();
printf( "&b = %p, b = %d\n", (void *) &b, b );
b++;
}
void bletch( void )
{
int c = 3;
bar();
printf( "&c = %p, c = %d\n", (void *) &c, c );
c++;
}
int main( void )
{
foo();
bar();
bletch();
bar();
foo();
return 0;
}
All of a
, b
, and c
are declared auto
(which is the default). When I build and run this code, I get the following output:
&a = 0x7fff701d234c, a = 1
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d230c, a = 1
&b = 0x7fff701d232c, b = 2
&c = 0x7fff701d234c, c = 3
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d234c, a = 1
New instances of a
, b
, and c
are created and initialized when their respective functions are entered and destroyed when the functions exit. They get different addresses depending on where the functions are in the call chain2.
If I change that code such that a
is declared
static int a = 1;
I get the following output:
&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 3
&b = 0x7fffd1e75cac, b = 2
&c = 0x7fffd1e75ccc, c = 3
&a = 0x500a58, a = 4
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 5
So, a couple of things are immediately apparent - the address of a
doesn't change with each call to foo
, and the value of a
is not re-initialized every call. Again, the instance for a
is created and initialized once when the program starts up, and that instance persists beyond the lifetime of foo
.
Declaring b
as static
gives us
&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x500a5c, b = 2
&a = 0x500a58, a = 3
&b = 0x500a5c, b = 3
&c = 0x7fffc301f8cc, c = 3
&a = 0x500a58, a = 4
&b = 0x500a5c, b = 4
&a = 0x500a58, a = 5
Don't read too much into the address values themselves; that all depends on the platform. Obviously, on my platform, static
items are stored in a very different location than auto
items, but another platform might not show as obvious a difference.
- In practice, the space for
auto
variables is taken from the runtime stack, although that's an implementation detail, not a language requirement.
- Notice how the same addresses may be re-used for different objects depending on where the function is in the call chain.