0

So this is one of the first times I've worked with multi-dimensional arrays in C. I have a 3D array of ints that is being filled and I want to check to make sure the values are turning out alright, but I'm having some issues after the array is filled. Some similar code of what's going on to explain the situation:

int a = 10;
int b = 20;
int c = 30;
int* valuesPtr;
valuesPtr = (int*) malloc(a * b * c * sizeof(int));
functionThatFillsValues(valuesPtr); // this function expects a *int, and works.

Everything here is working properly. Then, I'm printing out the value like so:

printf("0,0,0: %d\n", *valuesPtr);

This successfully prints the value at [0][0][0]. Doing the following works:

printf("0,0,1?: %d\n", *(valuesPtr+1));

Now, I'm to understand from https://www.geeksforgeeks.org/pointer-array-array-pointer/ there's a few ways to get values from where this pointer is pointing to. The following works: printf("0,0,0: %d\n", valuesPtr[0]); but as soon as I try to go any further than that, it breaks on make. None of the following work:

printf("0,0,0: %d\n", valuesPtr[0][0];
printf("0,0,0: %d\n", valuesPtr[0][0][0];
printf("0,1,0: %d\n", valuesPtr[0][1];
printf("0,0,1: %d\n", valuesPtr[0][0][1];
printf("%d\n", *(*(valuesPtr+1)+1));
printf("%d\n", *(*(*(valuesPtr+1)+1)+1));

With the [] ones getting the error "subscripted value is neither array nor pointer", and the *() attempts getting the error "invalid type argument of ‘unary *’ (have ‘int’)" I'm guessing because in both cases, it's resolving both valuesPtr[0] and *(valuesPtr) as an int, and then not going any further. I'm guessing the issue is that valuesPtr is declared as a *int, so it believes it's just a pointer to an int. How can I get this to understand it's a pointer to a 3D array, either by getting the pointers to the 2D or 1D arrays inside, or just getting it to understand I can use [][][] notation?

Would something like this be looking at the right spot?

printf("i,j,k: %d", *(valuesPtr+(i*(c*b))+(j*(c))+k)

SpaceMouse
  • 142
  • 8
  • 1
    You are allocating a flat array, which you can dereference only once with either `[]` or `*` syntax. The answer at [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) may have what you are looking for. – M Oehm May 26 '21 at 14:36
  • @m-oehm Unfortunately, the function can't take anything other than an *int - that's not something I have the power to change. So it has to be a flat array. But knowing it's a flat array, the idea I have at the end of the question should be looking at the right spot, yes? – SpaceMouse May 26 '21 at 14:41
  • 2
    @SpaceMouse - the idea was correct but the math is off. Since you're really using a flat array you have to calculate the other dimensions based on your original allocation. Something like this: printf("%d ", *(valuesPtr + (i*20*30)+(j*30)+(k))); – Jim Castro May 26 '21 at 14:54
  • 2
    You can still use the technique used in that answer if you alias the flat array to a contiguous multi-dimensional array: `int (*m)[20][10] = (int (*)[20][10]) valuesPtr;` Now you can index the array elements with `m[k][j][i]` syntax. [See an example here](https://ideone.com/pxsFm1). – M Oehm May 26 '21 at 15:15

2 Answers2

2

There are two different things you can do.

Option #1 is to simply use linear offsets to access your elements. For that, you'd have to compute a single offset from your triplet. Let me show you an example:

printf("2, 3, 4: %d\n", valuesPtr[(2 * b + 3) * c + 4]);

This is a bit cumbersome, but it's the traditional way of doing things, and it works.

An alternative, if you're using C99 or later, is to use what's called a variably-modified type. Note that this requires support for VLAs, which is optional since C11, and thus some implementations (notably, MSVC) might not support them.

If you declared the array manually, it'd be int arr[a][b][c];. Because of the array-to-pointer decay rules, this would become an int (*) [b][c] when used in an expression — that is, a pointer to an array of b arrays of c elements. So you can just declare your pointer type to be of that type:

int (* valuesPtr) [b][c] = malloc(sizeof *valuesPtr * a);
// note that, since each element is already an array of [b][c], you only
// need to multiply by a to get the final size
printf("0, 0, 0: %d\n", valuesPtr[0][0][0]);
printf("1, 0, 0: %d\n", valuesPtr[1][0][0]);
printf("0, 1, 0: %d\n", valuesPtr[0][1][0]);
printf("0, 0, 1: %d\n", valuesPtr[0][0][1]);
aaaaaa123456789
  • 5,541
  • 1
  • 20
  • 33
1

If I understand your problem correctly, you are working with a 3d data set. However, you are forced to utilize a 1D array that maps to a 3D array. Perhaps the following will help:

You might think about building a (free) union of a 1D array and a 3D array so that you may refer to the data in whichever manner is more convenient. That is, you can fill the 3D array using triple indices, but for 1D purposes you can locate the first integer in the linear array.

Since the size of the arrays is identical, the union will contain nothing extra, and it shouldn't cost much to reference your data in either way. A simple example:

// FreeUnion.c

#include <stdio.h>

#define     a   10      // defines for the sizing below
#define     b   20
#define     c   30

union blob {
    int a3d[a][b][c];   // a 3d array
    int a1d[a*b*c];     // a 1d array
} arrun;

void main(void) {
    arrun.a1d[0] = 0;
    arrun.a1d[1] = 1;
    arrun.a1d[2] = 2;

    printf("one: %i\n", arrun.a1d[1]);
    printf("triplet 0: %i, %i, %i \n", arrun.a3d[0][0][0],
                     arrun.a3d[0][0][1], arrun.a3d[0][0][2]);
}
Lacovara
  • 11
  • 1