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 statickeyword
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_varoccupied 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.