11

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???

Stas Jaro
  • 4,747
  • 5
  • 31
  • 53
  • 1
    You must only free arrays created with `calloc`, and free them in reverse order of allocation: First the row arrays, then the main one (if allocated with `calloc`). There should be one `free` call for each `calloc` call, so you don't free the individual floats. – alexis Jun 10 '13 at 21:42
  • @alexis: If I were to use this c array in an Objective-c class, then should I allocate it with calloc and free it or just use array[size]? – Stas Jaro Jun 10 '13 at 21:43
  • 1
    If you know the array size at compile time, I would declare the array instead of using `calloc`. This also goes for the rows. – alexis Jun 10 '13 at 21:44
  • 2
    Won't each `(float()){0,0,0}` go out of scope after each for iteration? – Dariusz Jun 10 '13 at 21:50
  • If you'll be using it in a class, I would most certainly declare it with array[size]. Managing objects with dynamically allocated state is a pain in the neck-- don't go there unless you have to. – alexis Jun 11 '13 at 19:55
  • @alexis I doubt it'll be used in a class. The question is tagged c and not c++. – Eric Finn Jun 11 '13 at 20:31
  • @Eric, look at the OP's first comment (above): "If I were to use this c array in an Objective-c class, then should I ..." – alexis Jun 11 '13 at 20:57
  • @alexis Ah. Very well then, carry on. – Eric Finn Jun 11 '13 at 23:24

3 Answers3

4

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!

alexis
  • 48,685
  • 16
  • 101
  • 161
2

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

Why do you want an array of pointers to arrays? Wouldn't an array of arrays be sufficient? (Keep in mind that arrays are already pointer-like, they are not passed by value, rather the address of the first element is passed when an array is passed as an argument to a function).

// stack allocation, no need to free
float array[SIZE][3]; 

for (int i = 0; i < SIZE; i++) {
    // do something with array[i][0], array[i][1], array[i][2]
}

Then how would I free the data?

In this case you wouldn't, since the data is stack allocated (will be cleaned up automatically once out of scope). Just remember the rule of thumb is that for every memory allocation you make, a corresponding free is necessary. So if you allocate memory for an array of floats, as in

float* arr = malloc(sizeof(float) * 3); // array of 3 floats

Then you only need to call free on the array that you malloc'd, no need to free the individual floats. If you perform nested allocation as in

// array of length SIZE, consisting of float pointers
float** arr = malloc(sizeof(float*) * SIZE);

// allocate the array of 3 floats at each index
for (int i = 0; i < SIZE; i++) 
    arr[i] = malloc(sizeof(float) * 3);

Then you will need to perform a free for every malloc, as in

// free the individual arrays
for (int i = 0; i < SIZE; i++)
    free(arr[i]);
// free the array of arrays
free(arr);

The lesson to take away here is to avoid dynamic allocation of arrays all-together. Stick with either std::vector or stack-allocated arrays.

Porkbutts
  • 924
  • 7
  • 12
2

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.
Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • Why would you declare a one-dimensional array and do all the array arithmetic yourself, when C provides great facilities for it? That's just wrong. – alexis Jun 11 '13 at 20:02
  • @alexis what would you rather do? allocate an array of pointers and then each row separately? – Dariusz Jun 11 '13 at 20:13
  • The OP _was_ asking about an array of pointers, which has valid uses-- and supporting two-dimensional array notation is one of them. In this particular case, if `SIZE` is not known in advance the right solution is to allocate a single _two_ dimensional array. See the last part of my answer. (But yes, I would sooner allocate an array of pointers than mess with index multiplication like this). – alexis Jun 11 '13 at 20:18
  • PS. If you're worried about cache optimization, you shouldn't be bothering with range checking. Nothing wrong with it in principle, but it's not the C way :-) – alexis Jun 11 '13 at 20:22
  • @alexis you think your answer is better for the OP, I get that. I also think it is perfectly valid. However, I think the same way about my answer too. It is different but has it's uses. It is definitely not *wrong*. And since I don't know for sure what the OP wants, I wrote that. The code there is just an example. I probably wouldn't check the boundaries in my app, but since OP seems to have trouble, I added that part. If you want to argue on the Internet, go to reddit or facebook. – Dariusz Jun 11 '13 at 20:28
  • 1
    I agree with much of what you just wrote. There's no point in us arguing about it, and I respect the fact that you're not convinced by my objections. Perhaps other SO users will weigh in with comments or votes to help settle the question. – alexis Jun 11 '13 at 21:08