How to return an array from a function in C?

Refresh

April 2019

Views

3k time

1

Most websites say something like this:

C programming does not allow to return an entire array as an argument to a function. However, you can return a pointer to an array by specifying the array's name without an index.

I've just begun with pointers and as far as I understand, a pointer variable is a variable which stores a memory address. When we dereference it using *, we get to that memory address and hold the value stored there. Also, in the case of an array, the pointer must point to the first element.

Now, if our function returns a pointer to the first element of our array as in this example:

int * myFunction() {
   .
   .
   .
}
  1. What and how are we going to dereference it?
  2. Does "the function returning a pointer" mean it is returning the memory address to which the pointer points?

In that case,

Second point to remember is that C does not advocate to return the address of a local variable to outside of the function, so you would have to define the local variable as static variable.

  1. What exactly is a static variable? [I surfed enough but didn't find anything statisfying]. Wikipedia defn.:

In computer programming, a static variable is a variable that has been allocated statically so that its lifetime or "extent" extends across the entire run of the program.

Another website says,

The static storage class instructs the compiler to keep a local variable in existence during the life-time of the program instead of creating and destroying it each time it comes into and goes out of scope.

Someone please give me a clear and basic explanation what a static variable really is and how it is relevant in this context(returning array from function).

I'm really confused.

3 answers

1

What and how are we going to dereference it?

The pointer variable returned by the function.

By using the appropriate operator *, an example:

int z = 5;
int* pointer_to_z = &z; // get memory address of z and store that in pointer_to_z
int can_i_have_my_z_back_please = *z; // check what value is at that memory address and store it, a copy is made here.

Does "the function returning a pointer" mean it is returning the memory address to which the pointer points?

It's returning a pointer variable, this variable holds the memory address of the value. Basically, "pointing to " a value is the same as "having it's address".

What exactly is a static variable? [I surfed enough but didn't find anything statisfying].

There's lots of good SO answers on what a static variable is already. To summarize (only going in on the lifetime of the variable and not it's linkage) a static variable is valid for the rest of the program once initialized, this means that it's lifetime is not scope bound like a local variable is:

void hello()
{
  int x = 5;
} // x destroyed here..

void hello_static()
{
  static int x = 5;
} // x will only be destroyed at the "end" of the program

This in turn means that it's completely safe to return a pointer (the memory address) of a local static variable since the static variable will still be accessible :

int* return_my_static()
{
  static int a = 5;
  return &a;
}

int main()
{
  int* pointer_to_static = return_my_static(); // get the memory address of the static
  printf("%i", *pointer_to_static); // print out the value by dereferencing
}

But, doing so for a local non-static variable will cause undefined behaviour as the variable pointed to (it's memory address) is no longer valid as it has been destroyed:

int* return_local()
{
  int a = 5;
  return &a;
} // a is destroyed here.. oopsies

int main()
{
     int* pointer_to_local = return_local(); // get the memory address of the local.
     //local variable has been destroyed now and 'pointer_to_static' now points to garbage memory!
      printf("%i", *pointer_to_local); // Try to print out the value by dereferencing, this is undefined behaviour, anything can happen.
}

Note that the above code might run and output the expected result but that is sheer luck, this is undefined behaviour and should be avoided at all costs since anything can happen at this point.

0

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").


  1. 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.

0

Question 3:

int a;

void foo() {
   int b
   static int c;
}

int main() {
    foo();
}

When the program starts, the memory for a and c are allocated and remain allocated until the program exits. So there is exactly on a and on c at any given time. Each time anyone (here main) calls foo, b is allocated on the stack until that function returns.

About the Quote in front of question 3:

Returning the address of a and c is no problem, because those exist as long as the program lasts, but returning an address to b is an error because as soon as the caller gets the pointer in his hand, the pointer points to invalid memory.

Question 1:

You dereference a pointer by putting an asterisk in front. It doesn't matter, that that pointer points to. It it is an array, you can increment or decrement the pointer to get to the index you're trying to reach like in a simple addition: *(p + 4) will access the 5th element (because *(p + 0) is the first one, so *(p + 1) is the second one and so on).

Instead of writing *(p + 4) you can also write p[4].

So assuming you function looks like this:

int * myFunction() {
    static int array[8];
    return array;
}

then the return statement will return the address of the array which is the exact same thing as the address of the first element of the array.

so, having int * p = myFunction(); you can then access the array with the intuitive syntax p[0] = 42; ...; p[7] = 23;

Question 2:

A function, that returns a pointer is a function that returns a pointer. A pointer is a thing, which points to a point in memory. Usually that is called a memory address, but the C language doesn't care. So, in practice, "the function returning a pointer" means it is returning the memory address to which the pointer points, yes.