2

I believe this code forces memory allocation for a 3-d array to be contiguous.

void ***calloc_3d_array(size_t n3, size_t n2, size_t n1, size_t size){
  void ***array;
  size_t i, j;

  if ((array = (void***)calloc(n3, sizeof(void**))) == NULL) {
    printf("[calloc_3d] failed to allocate memory for %d 1st-pointers\n",
              (int)n3);
    return NULL;
  }

  if ((array[0] = (void**)calloc(n3*n2, sizeof(void*))) == NULL) {
    printf("[calloc_3d] failed to allocate memory for %d 2nd-pointers\n",
              (int)(n3*n2));
    free((void*)array);
    return NULL;
  }

  for (i=1; i<n3; i++) {
    array[i] = (void**)((unsigned char*)array[0]+i*n2*sizeof(void*));
  }

  if ((array[0][0] = (void*)calloc(n3*n2*n1, size)) == NULL) {
    printf("[calloc_3d] failed to alloc. memory (%d X %d X %d of size %d)\n",
              (int)n3, (int)n2, (int)n1, (int)size);
    free((void*)array[0]);
    free((void*)array);
    return NULL;
  }

  for (j=1; j<n2; j++) {
    array[0][j] = (void**)((unsigned char*)array[0][j-1]+n1*size);
  }

  for (i = 1; i < n3; i++) {
    array[i][0] = (void**)((unsigned char*)array[i-1][0]+n2*n1*size);
    for (j = 1; j < n2; j++) {
      array[i][j] = (void**)((unsigned char*)array[i][j-1]+n1*size);
    }
  }

  return array;
}

I am trying to alter this into a function that allocates a 4-d array contiguously. I do not fully understand the 3-d case flawless, so abstracting to a 4th dimension is a little shaky. I mostly unsure exactly why in any loop we have either array[i] = (void**) or array[i][j] = (void**) in the 3d code, so in the 4d code I have all the array[i][j][k] = (void***). Here is what I currently have

void ****calloc_4d_array(size_t n4, size_t n3, size_t n2, size_t n1, size_t size){
  void ****array;
  size_t i, j, k;

  /* Alloc array of 3d pointers */
  if ((array = (void****)calloc(n4, sizeof(void***))) == NULL) {
    printf("[calloc_3d] failed to allocate memory for %d 1st-pointers\n",
              (int)n4);
    return NULL;
  }

  /* In first slot allocate a entire 2d pointer array */
  if ((array[0] = (void***)calloc(n4*n3, sizeof(void**))) == NULL) {
    printf("[calloc_3d] failed to allocate memory for %d 2nd-pointers\n",
              (int)(n4*n3));
    free((void*)array);
    return NULL;
  }

  /* Loop over slots and adjust address to accommodate 2d pointers */
  for (i = 1; i < n4; i++) {
    array[i] = (void***)((unsigned char*)array[0]+i*n3*sizeof(void**));
  }

  /* In the first 2d pointer, allocate the entire space for 1d pointers*/
  if ((array[0][0] = (void**)calloc(n4*n3*n2, sizeof(void*))) == NULL) {
    printf("[calloc_3d] failed to allocate memory for %d 3rd-pointers\n",
              (int)(n4*n3*n2));
    free((void*)array[0]);
    free((void*)array);
    return NULL;
  }

  /* Loop over other 2d slots and adjust address to accommodate type */
  for (j=1; j<n3; j++) {
    array[0][j] = (void**)((unsigned char*)array[0][j-1]+n2*size);
  }
  for (i=1; i<n4; i++) {
    array[i][0] = (void**)((unsigned char*)array[i-1][0]+n3*n2*size);
    for (j=1; j<n3; j++) {
      array[i][j] = (void**)((unsigned char*)array[i][j-1]+n2*size);
    }
  }

  /* Finally allocate for entire array */
  if ((array[0][0][0] = (void*)calloc(n4*n3*n2*n1, size)) == NULL) {
    printf("[calloc_3d] failed to alloc. memory (%d X %d X %d X %d of size %d)\n",
              (int)n4, (int)n3, (int)n2, (int) n1, (int)size);
    free((void*)array[0][0]);
    free((void*)array[0]);
    free((void*)array);
    return NULL;
  }


  for (k=1; k<n2; k++) {
    array[0][0][k] = (void***)((unsigned char*)array[0][0][k-1]+n1*size);
  }
  for (j=1; j<n3; j++) {
    array[0][j][0] = (void***)((unsigned char*)array[0][j-1][0]+n2*n1*size);
    for (k=1; k<n2; k++) {
      array[0][j][k] = (void***)((unsigned char*)array[0][j][k-1]+n1*size);
    }
  }
  for(i=1; i<n4; i++) {
    array[i][0][0] = (void***)((unsigned char*)array[i-1][0][0]+n3*n2*n1*size);
    for (j=1; j<n3; j++) {
      array[i][j][0] = (void***)((unsigned char*)array[i][j-1][0]+n2*n1*size);
      for (k=1; k<n2; k++) {
        array[i][j][k] = (void***)((unsigned char*)array[i][j][k-1]+n1*size);
      }
    }
  }

  return array;
}

Edit: The compiler gave me a warning relating to my (void***) question, and it seems to make sense that array[][] is a (void**), but I still don't know why it is happy with array[i] = (void***) instead of array[i] = (void*). Otherwise said, why is it (void*) with calloc array[0][0][0] = (void*)calloc(n4*n3*n2*n1, size), but (void***) when using the bit shifting/setting the address(?) array[0][0][k] = (void***)((unsigned char*)array[0][0][k-1]+n1*size);? I would think whatever kind of object array[][][] is either (void*) or (void***).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Novice C
  • 1,344
  • 2
  • 15
  • 27
  • 1
    Judging from what I see, the 3D array is not contiguous. I doubt if the 4D array is contiguous either. To some extent, it depends on what you want to be contiguous. But multiple memory allocations prevent contiguity. – Jonathan Leffler Mar 06 '19 at 02:30
  • What does `array[0][0] = (void*)calloc(n3*n2*n1, size))` do? I thought this allocated enough contiguous memory at the address of array[0][0] to be the entire 3d array of size n3*n2*n1*size? If all this hubbub isn't about being contiguous would either you be able to shed light why the author went through this trouble? And then the for loops are patching up the addresses to be the appropriate bytes away from the other indexes. – Novice C Mar 06 '19 at 02:33
  • 1
    Note that `****calloc_4d_array` is not a 4D array like `int p[2][3][5][7]` is. `calloc_4d_array` is a [pointer to pointer to pointer to pointer to void](https://cdecl.org/?q=void+****calloc_4d_array). – chux - Reinstate Monica Mar 06 '19 at 04:39
  • Related: [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/q/42094465/694733) – user694733 Mar 06 '19 at 07:38
  • If you want contiguous memory, you don't need arrays of pointers at all. See chux's comment. – Peter Cordes Apr 07 '22 at 04:32

1 Answers1

0

This should allocate 2 and above dimensions array continuously use recursion. Suggest use FORTRAN for such kind of high dimensional computation, this is not memory efficient. The example seems valgrind clean.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void *alloc_nd(int *dim, int nd, size_t size)
{
    assert(nd>=2);
    void **p = malloc(sizeof(void*)*dim[0]);
    if(nd==2) {
        p[0] = malloc(size*dim[0]*dim[1]);
        for(int i=1; i<dim[0]; i++)
            p[i] = p[i-1]+size*dim[1];
    } else {
        int xd[nd-1]; 
        for(int i=1; i<nd; i++)
            xd[i-1] = dim[i];
        xd[0] *= dim[0];        //callapse the 1st two dimension
        p[0] = alloc_nd(xd, nd-1, size);
        for(int i=1; i<dim[0]; i++)
            p[i] = p[i-1]+sizeof(void*)*dim[1];
    }
    return p;
}

void free_nd(void *p, int nd)
{
    if(nd==2) {
        free(((void**)p)[0]);
        free(p);
    } else {
        free_nd(((void**)p)[0], nd-1);
        free(p);
    }
}

int main()
{
    int dim[] = {3,4,5,6};
    int ****array;
    array = (int****)alloc_nd(dim, 4, sizeof(int));
    for(int i0=0; i0<dim[0]; i0++)
        for(int i1=0; i1<dim[1]; i1++)
            for(int i2=0; i2<dim[2]; i2++)
                for(int i3=0; i3<dim[3]; i3++)
                    array[i0][i1][i2][i3] = i0+i1+i2+i3;
    int *p = &array[0][0][0][0];  //do you mean continuous in this way? 
    for(int i=0; i<dim[0]*dim[1]*dim[2]*dim[3]; i++)
        printf("p[%5d]=%d\n", i, p[i]);
    free_nd(array, 4);
    return 0;
}
KL-Yang
  • 381
  • 4
  • 8