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!

6
  • 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);. Commented May 6, 2018 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? Commented May 6, 2018 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. Commented May 6, 2018 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. Commented May 6, 2018 at 18:22
  • 3
    this answer should answer most questions on the topic of arrays, pointers to them and allocating them. Commented May 6, 2018 at 18:26

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

That amount of effort and explanation always deserves a solute (comments included).
@DavidC.Rankin well thanks! I intended to put all this advice in an answer directly, but somehow, I wasn't capable of spotting the obvious error in the original code yesterday, no idea why ;)
Sometimes -- you just have to sleep on it :)
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.

4 Comments

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.
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.
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.
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 :)
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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.