Going back to front:
What exactly is a static variable?
An object with static
storage duration has storage allocated for it when the program first starts up, and that storage is held until the program exits. An object has static
storage duration if:
- its identifier is declared at file scope (outside the body of any function); or
- its identifier is declared with the
static
keyword
Example:
#include <stdio.h>
int g_var; // static storage duration
void foo( void )
{
static int s_var = 10; // static storage duration
int l_var = 10; // auto storage duration
printf( "g_var = %d, s_var = %d, l_var = %d\n", g_var++, s_var++, l_var );
}
In this snippet, both g_var
and s_var
have static
storage duration; g_var
because it was declared at file scope, s_var
because it was declared with the static
keyword. By virtue of having static
storage duration, g_var
is implicitly initialized to 0. Note that s_var
is initialized once at program startup - it will not be re-initialized to 10
on subsequent calls to foo
. Thus, each time you call foo
, the output will be
g_var = 0, s_var = 10, l_var = 10
g_var = 1, s_var = 11, l_var = 10
g_var = 2, s_var = 12, l_var = 10
...
l_var
has auto
storage duration - its lifetime is limited to the lifetime of the foo
function. Each time foo
is called, storage for a new instance of l_var
is allocated and initialized at function entry and released when the function exits. This is important - l_var
ceases to exist when foo
exits, so any pointer to it will become invalid when foo
returns1.
This is why you can't do something like
int * bar( void )
{
int array[N];
...
return array;
}
because array
ceases to exist once bar
exits, and the pointer that gets returned is invalid.
Now, one way around this is to declare the array as static
:
int * bar( void )
{
static int array[N];
...
return array;
}
In this case, array
doesn't go away when the function exits, so the pointer is still valid.
However...
This creates other problems. Only a single instance of array
is ever created, and it contains the last thing written to it by another call to bar
. The code is no longer re-entrant; it can't safely be interrupted in mid-execution, then called by another function before the first invocation has completed. Creating a static
array just so you can cleanly return a pointer to it is usually the wrong answer.
Either pass a target array as an argument to the function:
void foo( int *array, size_t arraySize )
{
...
array[i] = some_value;
...
}
or dynamically allocate an array and return the pointer to it:
int * bar( void )
{
int *array = malloc( sizeof *array * N );
if ( array )
{
// initialize array contents
}
return array;
}
The problem with this is that someone else is responsible for releasing that memory when you're done.
Does "the function returning a pointer" mean it is returning the memory address to which the pointer points?
The function returns the value of a pointer, which is the address of another object. In the code above, bar
returns the value of the expression array
, which turns out the be the address of the first element of array
.
In the second case of bar
above, the value being returned is equivalent to &array[0]
.
What and how are we going to dereference it?
You can dereference a pointer in two ways - using the *
dereference operator, or using the []
subscript operator.
The subscript operation a[i]
is defined as *(a + i)
- given the address a
, offset i
elements (not bytes) from a
and dereference the result. So you can take the pointer returned from bar
and do the following with it:
int *p = bar();
printf( "p[0] = %d\n", *p );
printf( "p[0] = %d\n", *(p + 0) );
printf( "p[0] = %d\n", p[0] );
So, does this mean arrays and pointers are the same thing? No. Arrays are not pointers; however, under most circumstances, an array expression (i.e., an expression of type "N-element array of T
") will be converted ("decay") to a pointer expression ("pointer to T
").
- Obviously, the memory location that
l_var
occupied still exists, so the value of the pointer doesn't suddenly become garbage or anything like that; however, that memory location is now available for something else to use, and if you try to read or write to that location you can cause problems.