Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to allocate and free arrays of pointers to arrays

Tags:

arrays

c

pointers

I want to create an array of pointers to arrays of 3 floats. What is the correct way to do this?

float *array1[SIZE]; // I think it is automatically allocated
// OR
float **array1 = calloc(SIZE, sizeof(float*));
free(array1);

for (int i = 0; i < SIZE; i++) {
    array1[i] = (float[]){0,0,0};
    // OR
    array1[i] = calloc(3, sizeof(float));
}

Then how would I free the data? I'm pretty sure just free(array1); wouldn't work, so would I free each pointer in the array then free the array, or since I allocated three floats, would I free each float, then each 3 float array, then the whole array???

like image 206
Stas Jaro Avatar asked Jun 10 '13 21:06

Stas Jaro


2 Answers

If you know the array size at compile time (and you do, if SIZE is a compile-time constant), you should just declare a two-dimensional array. You don't need to free this at all (and must not).

float array1[SIZE][3];

You need to use calloc, and to create an array of pointers, only if the dimensions are not known at compile time. In this case, there should be one call to free for each call to calloc. And since you cannot use an array after you free it, you need to free the row arrays before you free array1.

float **array1 = calloc(nrows, sizeof (float *));
for (int i=0; i < nrows; i++)
    array1[i] = calloc(3, sizeof(float));
// Use it...

// Now free it
for (int i=0; i < nrows; i++)
    free(array1[i]);
free(array1);

Edit: if you won't be rearranging the pointers (to sort the rows in-place, for example), you can do all of this with just one calloc (and one call to free afterwards):

float (*array1)[3] = calloc(3*nrows, sizeof (float));

That's because the number of columns is known at compile-time, and that's all the pointer arithmetic needs to know. Then you can write things like array1[i][j], and you can still pass around array1[i] as if it was a real pointer to a row. C is great that way, take advantage of it!

like image 200
alexis Avatar answered Nov 27 '22 18:11

alexis


A general rule is that for each time you call malloc() or calloc() you will need to do a free() call on the returned pointer.

If you want a two dimensional array with compile-time known size, just use a two dimensional array! float val[5][3] is perfectly valid.

If you want a two dimensional array and you don't know it's size during compile-time, you most probably want to use a standard, single diemensional calloc() and an appropriate getter.

#define ARR_COLUMNS 10
#define ARR_ROWS 10
float* arr = calloc (ARR_COLUMNS * ARR_ROWS, sizeof(float));

int get(float* arr, int x, int y) {
  if (x<0 || x>= ARR_COLUMNS) return 0;
  if (y<0 || y>= ARR_ROWS) return 0;
  return arr[ARR_COLUMNS*y+x];
}

void set (int* arr, int x, int y, float val) {
  if (x<0 || x>= ARR_COLUMNS) return;
  if (y<0 || y>= ARR_ROWS) return;
  arr[ARR_COLUMNS*y+x] = val;
}

Of course replace the defines with appropriate variables.

By doing so you will:

  • save yourself costly allocs and frees
  • have less fragmented memory
  • simplify your possible realloc calls
  • ensure the data is cached better and accessed without the common [x][y] vs [y][x] iteration cache problem.
like image 28
Dariusz Avatar answered Nov 27 '22 18:11

Dariusz