2

I'm trying to create a 3D array of ints initialized to zeros each of fixed size denoted as "dim".

For example, for dim=3, it will have 27 cells for ints.

I tried this:

int ***board;
int **rows;
int *tried;
board = calloc(dim,sizeof(int**));
rows = calloc(dim*dim, sizeof(int*));
tried = calloc(dim*dim*dim, sizeof(int));
int i;
int j;
int k;
for (i=0 ; i<dim ; i++) {
    board[i] = rows + i*dim*dim;
    for (j=0 ; j<dim ; j++) {
        board[i][j] = tried + j*dim + i*dim*dim;
        }
}
for (i=0 ; i<dim ; i++) {
    for (j=0 ; j<dim ; j++) {
        for (k=0 ; k<dim ; k++) {
            board[i][j][k] = 0;
        }
    }
}

Trying to debug it, I found that it works until:

board[1][1][0] = 0

And then the program gets stuck and i just can't find the reason.

Can someone explain this please?

Thanks!

  • 1
    this is **not** a 3d array. It would look simply like this `int board[dim][dim][dim];` or, dynamically allocated: `int (*board)[dim][dim] = malloc(dim * sizeof *board);`. –  May 06 '18 at 18:15
  • i'm not sure i get you, you mean i could replace all the code except of the 7 last lines with that second line you wrote and it will work fine? – user183748292 May 06 '18 at 18:18
  • 1
    exactly that, given your compiler supports VLAs (variable length arrays). If not, `dim` would have to be a compile-time constant for this to work. –  May 06 '18 at 18:21
  • 2
    Oh, and using `calloc()` would already zero out all of it, so if you write `int (*board)[dim][dim] = calloc(dim, sizeof *board);`, there's no need for your clearing loops either. –  May 06 '18 at 18:22
  • 3
    [this answer](https://stackoverflow.com/a/42094467/2371524) should answer most questions on the topic of arrays, pointers to them and allocating them. –  May 06 '18 at 18:26
  • OK i will double check everything and check out that link. thanks. – user183748292 May 06 '18 at 18:37

3 Answers3

4

First about the error in your code. Compare this:

rows = calloc(dim*dim, sizeof(int*));

to this:

for (i=0 ; i<dim ; i++) {
    board[i] = rows + i*dim*dim;

The entire size of the array allocated to rows is dim*dim elements. So, already in the second iteration of this loop, you access it out of bounds. You probably meant:

for (i=0 ; i<dim ; i++) {
    board[i] = rows + i*dim;

As I already mentioned in the comment, this is not a 3D array. It mimics the usage in code by using pointers and you're using a kind-of clever trick here, so you only need 3 allocations in total. This might be a good idea under the following conditions:

  • your dim is variable at runtime, so you can't know it in advance, and
  • you have to write code for compilers that don't support VLAs1) (variable-length-arrays).

If one of this conditions is not true, it's much better to use a real 3D array. If the array doesn't have to live after leaving your function and the size isn't huge, just use a simple variable with automatic storage duration like

int board[3][3][3] = { 0 }; // possibly #define the dimension

or, for a variable dim, requiring a compiler supporting VLAs

int board[dim][dim][dim] = { 0 };

If on the other hand, the array will be huge and/or you need to return it from your function, you indeed have to allocate it dynamically. Then just use the following:

int (*board)[3][3] = calloc(3, sizeof *board); // static size
int (*board)[dim][dim] = calloc(dim, sizeof *board); // dynamic case, with VLA suppport

Also note that calloc() already sets your allocated memory to 0, so no need for looping all over it.


Side notes:

  • with sizeof, prefer the expression form, so instead of writing

    int *a = calloc(5, sizeof(int));
    

    better write

    int *a = calloc(5, sizeof *a);
    

    this avoids errors when you later change the type of a.

  • always check the return value of malloc() and friends -- they might return a null pointer (e.g. when you're running out of memory).


1) VLAs don't exist in the oldest standards C89/C90 -- they were introduced in C99 as a mandatory feature, but later made optional in C11. This allows C11 compilers to omit them, which might make sense when e.g. targeting embedded systems. In practice, you can safely assume a C11 compliant compiler supports them if it isn't special purpose.

0

I rewrote your code to show how allocation of a 3D array could look like. And as pointed out in the comments, there's no need to initialize the array to 0 since calloc does that for you. Had you used malloc the array would not have been initialized.

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

#define dim (3u)

int main() {

    int x;
    int y;
    int z;

    int ***cube;
    cube = calloc(dim, sizeof(int**));

    for (z = 0; z < dim; z++) {
        cube[z] = calloc(dim, sizeof(int*));
        for (y = 0; y < dim; y++) {
            cube[z][y] = calloc(dim, sizeof(int));
        }
    }

    for (z = 0; z < dim; z++) {
        for (y = 0; y < dim; y++) {
            for (x = 0; x < dim; x++) {
                cube[z][y][x] = z + y + x;
            }
        }
    }

    for (z = 0; z < dim; z++) {
        for (y = 0; y < dim; y++) {
            for (x = 0; x < dim; x++) {
                printf("%d ", cube[z][y][x]);
            }
            printf("\n");
        }
        printf("\n");
    }

    return 0;
}

What you want to store in it is up to you, in my example I wrote the sum of the counter to each index.

Witnessthis
  • 136
  • 4
  • 2
    Well this **still** isn't a 3D array and it takes an exponential amount of allocations. A *real* 3D array would just take one single allocation, OP tried a scheme to get away with 3 allocations. –  May 06 '18 at 19:03
  • I interpreted the question as "how do I create a data structure that I can index with foo[z][y][x]?" And sure, to free the memory one would have to call free multiple times. – Witnessthis May 06 '18 at 19:08
  • 1
    The OP code tries to allocate a flat array of the required size and then link pointers to that. That's an approach I've seen before, I'm not a fan of it, but it has the advantage to work even without a compiler understanding VLAs and still doesn't require a huge amount of allocations -- only 3 in this case. Your "classic" approach works, but needs more than `dim*dim` allocations, that's all I'm saying here :) And, of course, both aren't *3d arrays*. –  May 06 '18 at 19:13
  • 1
    If there's no **strong** reason to do otherwise, the best thing to do is to allocate a *real 3d array*, that's done in a single line of code like I commented on the original question :) –  May 06 '18 at 19:14
0

Code below is Unlicense.

I will suggest something different. Just create a 1D array and set some boundaries to interpret it as 3D. I added some test cases for you to better visualize how it works. Do not forget to look at how easy 'calloc' call is. Here is the code:

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

int getindex(int dim, int x, int y, int z) {
  return z * dim * dim + y * dim + x;
}

void printarray(int* tdarray, int dim) {
  printf("[\n");
    for (int i = 0; i < dim; i++) {
      printf("\t[\n");
      for (int j = 0; j < dim; j++) {   
      printf("\t\t[");
      for (int k = 0; k < dim; k++) {
        if (k == 0) printf("%d", *(tdarray + getindex(dim, k, j, i)));
        else printf(",\t %d", *(tdarray + getindex(dim, k, j, i)));
      } 
      printf("]\n");
    }
    printf("\n\t]\n");
  }
  printf("]\n");
}

int main() {
  int dim = 10;
  size_t arraysize = sizeof (int) * dim * dim * dim;

  int lookupindex = getindex(dim, 7, 5, 4); /* Numbers picked randomly */

  int* tdarray = (int*) malloc(arraysize);

  calloc(*tdarray, arraysize);

  /* Below is test code and visualizations, all magic happens above.*/

  if (*(tdarray + lookupindex) == 0) *(tdarray + lookupindex) = 7;

  printf("tdarray[x:%d, y:%d, z:%d]:\t%d\n\n", 7, 5, 4, *(tdarray + lookupindex));

  printarray(tdarray, dim);

  printf("\n\n\n\n\n\n\n\n\n\n");

  for (int i = 0; i < getindex(dim, 9, 9, 9) + 1; i++) *(tdarray + i) = i;

  printarray(tdarray, dim);

  free(tdarray);
}
Ozgur Bagci
  • 768
  • 11
  • 25