0

For a multitude of reasons, I'd like to allocate multidimensional arrays in contiguous chunks of memory. I can do this by allocating them manually, eg:

t.versions=(char***)malloc(sizeof(char**)*4);
t.versions[0]=(char**)malloc(sizeof(char*)*t.size*4);
t.versions[0][0]=(char*)calloc(t.size*t.size*4,sizeof(char));
for (i=1; i<t.size*4; ++i) 
    t.versions[0][i]=t.versions[0][i-1]+t.size;
for (i=1; i<4; ++i) 
    t.versions[i]=t.versions[i-1]+t.size;

Among other benefits, this solution simplifies freeing the allocated memory:

void contiguous_array_free(void** ptr, int depth)
{
    int *ptr_d;
    ptr_d=(int*)*ptr;
    if (depth>1)
        contiguous_array_free((void**)ptr_d, depth-1);
    free(ptr);
}
//(elsewhere in the code)
contiguous_array_free((void**)(*tile).versions, 3);

Now, I've got a small problem with allocating those arrays - while the approach posted above does work, ideally I'd like to have a generic solution that allows me to allocate those arrays with a single function call.

However, my attempt to achieve that goal results in the program crashing every time the array contents are used.

//dimension points to a 1-dimensional array of integers
//specifying the size in each array dimension
void* contiguous_array_alloc(int* dimension, int depth, int size)
{
    int i;
    char** ptr;
    if (depth==1)
    {
        ptr=(char**)malloc(*dimension*size);
        return ptr;
    }
    ptr=(char**)malloc(*dimension*sizeof(char*));
    *(dimension+1)*=*dimension;
    ptr[0]=(char*)contiguous_array_alloc(dimension+1, depth-1, size);
    *(dimension+1)/=(*dimension);
    for (i=1; i<*dimension; ++i)
        ptr[i]=ptr[i-1]+(*(dimension+1)*size);
    return (void*)ptr;
}

//(later in the code) (
int dimension[3];
dimension[0]=4;
dimension[1]=t.size;
dimension[2]=t.size;
t.versions=(char***)contiguous_array_alloc(&dimension[0], 3, sizeof(char));

Adding some debug messages into the code seems to indicate the elements are allocated correctly:

Allocating [4][9][9] array of size 1 elements; malloc()ating 16 byte array for 4 pointers; Allocated pointer array to level 2 at 003E29E8;

Allocating [36][9] array of size 1 elements; malloc()ating 144 byte array for 36 pointers; Allocated pointer array to level 1 at 003E5728;

Allocating [324] array of size 1 elements;

324 byte data array at 003E57C0; Pointed data at 003E57C0; increasing every pointer by 9; Returning allocated array;

Pointed data at 003E5728; increasing every pointer by 9; Returning allocated array;

Allocated contiguous array at 003E29E8;

What is causing this behavior? I've checked the code several times and have no idea what I've done wrong.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • 1
    *"I'd like to allocate multidimensional arrays in contiguous chunks of memory"* AFAIK, you can't decide that, this is up to the OS. – m0skit0 Feb 27 '13 at 15:12
  • 6
    [Obligatory...](http://c2.com/cgi/wiki?ThreeStarProgrammer) – Mysticial Feb 27 '13 at 15:14
  • Why are you modifying the `dimension` array? – WhozCraig Feb 27 '13 at 15:14
  • [This answers the question](http://stackoverflow.com/questions/12462615/how-do-i-correctly-set-up-access-and-free-a-multidimensional-array-in-c) though doesn't explain issues specific to this post. – Lundin Feb 27 '13 at 15:17
  • m0skit0, to be pedantic: I know the closest I can get to my goal is reducing the amount of malloc() calls needed to get there to no more than *n* (where *n* is the number of dimensions in the array). WhozCraig, as I noted in the comment it's a one dimensional array of integers containing the size of the desired array in each dimension. The intended output of the function in the example is a [4][t.size][t.size] array of *char*s . – Michał Gawlas Feb 27 '13 at 15:40
  • Can you show an SSCCE ([Short, Self-Contained, Correct Example](http://sscce.org/)) with your structures detailed, which invokes allocation, initializes the data, prints the data, and frees the data? It looks a bit as though you're allocating a 3D array of single characters; is that correct? – Jonathan Leffler Feb 27 '13 at 15:44
  • 1
    Your top-level and intermediate arays of pointers are not set up correctly. They point to arrays of pointers, not to something that contains your data. Spacing them apart by `something*sizeof(yourdata)` is clearly an error. – n. m. could be an AI Feb 27 '13 at 15:54

2 Answers2

1

I think there is something wrong for ptr[i]=ptr[i-1]+(*(dimension+1)*size); this kind of pointer operation usage does not make sense. I modified the code as below which passed the test for 4-dimension array.

//dimension points to a 1-dimensional array of integers
//specifying the size in each array dimension
void* contiguous_array_alloc(int* dimension, int depth, int size) {
  int i;
  if (depth==2) {
    char ** ptr=(char **)malloc(*dimension * sizeof(void*));
    ptr[0]=(char *)malloc(*dimension * dimension[1] * size);
    for (i=1; i<*dimension; ++i) {
      ptr[i]=ptr[i-1]+(*(dimension+1) * size);
    }
    return (void*)ptr;
  } else {
    void ***ptr=(void***)malloc(*dimension * sizeof(void*));
    *(dimension+1)*=(*dimension);
    ptr[0]=contiguous_array_alloc(dimension+1, depth-1, size);
    *(dimension+1)/=(*dimension);
    for (i=1; i<*dimension; ++i) {
      ptr[i]=ptr[i-1]+(*(dimension+1));
    }
    return (void*)ptr;
  }
}
fLOyd
  • 385
  • 1
  • 2
  • 9
  • Thank you. That was indeed the problem here: I forgot that p+x (where p is a pointer and x is an integer) equals p[x] and not xth byte after p. – Michał Gawlas Feb 27 '13 at 19:14
0

All you need for an abcd array of int is:

int (*p)[b][c][d] = calloc(a, sizeof *p);
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I think you are right somehow. But the Michał Gawlas's multi-dimension array design is easy to use when you want to pass it to functions other than where you calloced it. Access can be only done by memory calculation in other function if you simply assign a contiguous memory. By contract, in this implementation, you can simply using some kind of ptr[i][j][k] style to access the element anywhere. – fLOyd Feb 27 '13 at 18:28
  • 1
    @flOyd: C supports variable-length arrays. You can pass the pointer and access it in other functions by passing it in parameters like this: `void /* or other type*/ foo(size_t b, size_t c, size_t d, int (*p)[b][c][d], /* other parameters */)`. There is simply no need for this nonsense about multiple levels of pointers. – Eric Postpischil Feb 27 '13 at 18:39
  • Thanks for pointing this out - I was avoiding that because I remembered it didn't work when I tried doing it once ago. – Michał Gawlas Mar 02 '13 at 01:24