lorenzattractor
lorenzattractor

Reputation: 107

Understanding pointer to pointer arrays as arguments in a function

While trying to learn C by myself, I came across this simple program that I want to develop. It just tries to make use of pointer to pointer arrays to make something that resembles matrices. I'm compiling on Windows and when I run it, it just crashes, meanwhile, trying this code on Linux it says segmentation fault, is this because of the function arguments that are arrays? What am I doing wrong here?

#include <stdio.h>
#include <stdlib.h>

void initializeArray(float** array, int size);
void printArray(float** array, int size);

int main()
{
    float** array_1 = NULL;
    int array_size = 3;

    initializeArray(array_1, array_size);

    // Free memory from array
    for (int i = 0; i < array_size; i++)
    {
        free(array_1[i]);
    }

    free(array_1);

    return 0;
}

void initializeArray(float** array, int size)
{
    array = malloc(size * sizeof(float*));

    if (array)
    {
        for (int i = 0; i < size; i++)
        {
            array[i] = malloc(size * sizeof(float));
            if (!array[i])
            {
                exit(0);
            }
        }
    }

    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            array[i][j] = 0;
        }
    }
}


void printArray(float** array, int size)
{
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            printf("%f\t", array[i][j]);
        }

        printf("\n");
    }
}

Upvotes: 0

Views: 73

Answers (2)

John Bode
John Bode

Reputation: 123448

If you want a function to modify the value of a parameter, you must pass a pointer to that parameter:

void foo( T *ptr )
{
  *ptr = new_value(); // write a new value to the thing ptr points to
}

void bar( void )
{
  T var;
  foo( &var ); // write a new value to var 
}

This is true for any type T, including pointer types. Replace T with P *, and we get

void foo( P **ptr )
{
  *ptr = new_value(); // write a new value to the thing ptr points to
}

void bar( void )
{
  P *var;
  foo( &var ); // write a new *pointer* value to var
}

Basically, whatever the type of var, you need one more level of indirection for ptr.

Applying that to your code:

void initializeArray(float*** array, int size)
{
    *array = malloc(size * sizeof(float*));

    if (*array)
    {
        for (int i = 0; i < size; i++)
        {
            (*array)[i] = malloc(size * sizeof(float)); // parens matter; you want
            if (!(*array)[i])                           // to index into what array *points
            {                                           // to*, not array itself
                exit(0);
            }
        }
    }

    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            (*array)[i][j] = 0;
        }
    }
}

which would be called from main as:

initializeArray(&array_1, array_size);

A couple of suggestions:

First, when calling malloc, make the operand of the sizeof operator your dereferenced target, rather than a type name:

ptr = malloc( N * sizeof *ptr );

In your case, it would be

*array = malloc( size * sizeof **array ); // sizeof operand has one more level of 
                                          // indirection than target

and

(*array)[i] = malloc( size * sizeof *(*array)[i] );

This will protect you if you change the type of array; you don't have to chase down every instance of sizeof (float) or sizeof (float *) and change those.

Secondly, what you're allocating isn't a 2D array - it's an array of pointers, each of which points to a separate array of float. Which is perfectly fine, depending on what you're doing, just be aware that the rows are not adjacent in memory - the object following array[1][2] is not going to be array[2][0].

If you want to allocate a contiguous, multi-dimensional array, you'd use something like

float (*array)[3] = malloc( 3 * sizeof *array );

That sets aside space for a contiguous 3x3 array. With VLA syntax, you could write a function like

void initializeArray( size_t rows, size_t cols, float (**array)[cols] )
{
  *array = malloc( rows * sizeof **array );
  if ( *array )
  {
    for ( size_t i = 0; i < rows; i++ )
      for ( size_t j = 0; j < rows; j++ )
        (*array)[i][j] = initial_value();
  }
}

Upvotes: 0

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140158

when doing:

void initializeArray(float** array, int size)
{
    array = malloc(size * sizeof(float*));

you're not changing array outside the function so array_1 points to NULL after (like before) the call (and creates a memory leak). You need to return it (or to pass it as triple *** pointer and use it as *array, but that's less convenient).

float **initializeArray(int size)
{
    float** array = malloc(size * sizeof(float*));
   ...
    return array;
}

and from main:

array_1 = initializeArray(array_size);

Upvotes: 1

Related Questions