0

I need to allocate contiguous space for a 3D array. (EDIT:) I GUESS I SHOULD HAVE MADE THIS CLEAR IN THE FIRST PLACE but in the actual production code, I will not know the dimensions of the array until run time. I provided them as constants in my toy code below just to keep things simple. I know the potential problems of insisting on contiguous space, but I just have to have it. I have seen how to do this for a 2D array, but apparently I don't understand how to extend the pattern to 3D. When I call the function to free up the memory, free_3d_arr, I get an error:

lowest lvl
mid lvl
a.out(2248,0x7fff72d37000) malloc: *** error for object 0x7fab1a403310: pointer being freed was not allocated

Would appreciate it if anyone could tell me what the fix is. Code is here:

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

int ***calloc_3d_arr(int sizes[3]){

   int ***a;
   int i,j;

   a = calloc(sizes[0],sizeof(int**)); 
   a[0] = calloc(sizes[0]*sizes[1],sizeof(int*));
   a[0][0] = calloc(sizes[0]*sizes[1]*sizes[2],sizeof(int));

   for (j=0; j<sizes[0]; j++) {
      a[j] = (int**)(a[0][0]+sizes[1]*sizes[2]*j);
      for (i=0; i<sizes[1]; i++) {
         a[j][i] = (int*)(a[j]) + sizes[2]*i;
      }
   }

   return a;

}



void free_3d_arr(int ***arr) {

   printf("lowest lvl\n");
   free(arr[0][0]);
   printf("mid lvl\n");
   free(arr[0]);         // <--- This is a problem line, apparently.
   printf("highest lvl\n");
   free(arr);

}



int main() {

   int ***a;
   int sz[] = {5,4,3};
   int i,j,k;

   a = calloc_3d_arr(sz);

   // do stuff with a

   free_3d_arr(a);

}
bob.sacamento
  • 6,283
  • 10
  • 56
  • 115
  • 1
    `a[0] = ...` followed by `a[j] = ...` seems awkward... – Oliver Charlesworth Aug 23 '16 at 18:16
  • 1
    There is no 3D array, nothing which can be used as one, nor point to one in your code! A pointer is not an array! If you need a 3D array, use one! Oh, and being a 3-star C-programmer is not a compliment. – too honest for this site Aug 23 '16 at 18:22
  • 1
    That said, SO is not a crowdsourced debugger - you should really invest in debugging this yourself. – Oliver Charlesworth Aug 23 '16 at 18:22
  • 1
    Does the implementation in use support VLAs? If yes, then just do `int array3D[x][y][z];` – alk Aug 23 '16 at 18:31
  • @Olaf In the actual code I need to write and run, a fixed size array is not an option. Will not know dims until run time. – bob.sacamento Aug 23 '16 at 18:33
  • @bob.sacamento: And what is the problem? Use a VLA. Said that, it does not change that you do not have a 3D array! – too honest for this site Aug 23 '16 at 18:35
  • @Olaf: This "*3-star C-programmer*" bashing simply isn't necessary ... - not just picking on you, BTW. ;-) – alk Aug 23 '16 at 18:39
  • @alk: 1) It is a hint, not bashing. 2) I don't think the hint is unnecessary. (Almost) all 3-star code has badly designed interfaces. While there **might have** been reasons until ca. 17 years ago (ok, minus 2-3 years) , there hardly is a reason nowadays with VLAs. And even that time, a 1D array with "manual" indexing was a better approach. How would you go with a 5D array? `int *****a`? – too honest for this site Aug 23 '16 at 18:42
  • 2
    Not objecting anything of what you say. But still, this "bashing" comment without any more context doesn't help nobody. Because either you already know it, or you don't understand it. Punkt! @Olaf – alk Aug 23 '16 at 18:46
  • @alk: [google "three star programmer"](https://www.google.de/search?q=three+star+programmer) -> first entry: http://c2.com/cgi/wiki?ThreeStarProgrammer – too honest for this site Aug 23 '16 at 18:52
  • 1
    Have you considered instead of insisting on 3-D array, use a 1-D array and polynomial index? This makes the logic for memory allocation simpler and more flexible when you index the array. for example, instead of `a3[i][j][k]` you can have `a1[i * n * m + j * m + k]` and you only need to allocate a 1-D array `a1` of size `n * m * l`, where n, m, l are the three dimensions. Depending on your memory access pattern, there are alternative ways to write the index for more efficient access. You can make it into a function `index(i, j, k, n, m)`. This is pretty much the standard approach. – Yan Zhou Dec 23 '16 at 14:44

2 Answers2

3

Since you are using C, I would suggest that you use real multidimensional arrays:

int (*a)[sz[1]][sz[2]] = calloc(sz[0], sizeof(*a));

This allocates contiguous storage for your 3D array. Note that the sizes can be dynamic since C99. You access this array exactly as you would with your pointer arrays:

for(int i = 0; i < sz[0]; i++) {
    for(int j = 0; j < sz[1]; j++) {
        for(int k = 0; k < sz[2]; k++) {
            a[i][j][k] = 42;
        }
    }
}

However, there are no pointer arrays under the hood, the indexing is done by the magic of pointer arithmetic and array-pointer-decay. And since a single calloc() was used to allocate the thing, a single free() suffices to get rid of it:

free(a);    //that's it.
cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • 2
    Nitpicking: All those `int`s should be `size_t`s. – alk Aug 23 '16 at 18:36
  • 1
    Slight addition: C11 made VLAs optional (main suspect is a large company which notoriously is not able to support C99 and some embedded compiler vendors who sell expensive tools which are C90 compatible only). Said that, a modern compiler will support VLAs. – too honest for this site Aug 23 '16 at 18:39
  • @Bob__ Thanks :-) I always forget that `calloc()` uses this *interesting* splitting of its size argument which `malloc()` doesn't. But I fixed that now. – cmaster - reinstate monica Aug 23 '16 at 20:13
  • 1
    @alk Oh, but they are compared to the elements of `sz` which is an array of `int`. You don't want me to compare signed against unsigned, do you ;-) (Of course I think you are right, `sz` should be defined as array of `size_t`, but I went with the definition of the OP for this.) – cmaster - reinstate monica Aug 23 '16 at 20:16
1

You can do something like this:

int ***allocateLinearMemory(int x, int y, int z)
{
    int *p = (int*) malloc(x * y * z * sizeof(int));
    int ***q = (int***) malloc(x * sizeof(int**));
    for (int i = 0; i < x; i++)
    {
        q[i] = (int**) malloc(y * sizeof(int*));
        for (int j = 0; j < y; j++)
        {
            int idx = x*j + x*y*i;
            q[i][j] = &p[idx];
        }
    }
    return q;
} 

void deallocateLinearMemory(int x, int ***q)
{
    free(q[0][0]);
    for(int i = 0; i < x; i++)
    {
        free(q[i]);
    }
    free(q);    
}

I use it and works fine.