2

How do I create a pointer to a 2D array of pointers? I'm trying to mutate a 2D array with different methods outside of main as well as work with it inside of main. I've had luck creating a pointer to a 2D array but I can't seem to initiate all indexes of the array to null. I can't seem to assign values either unless I pass the array to a method. This is what I tried:

BoardObject ** board;
board = malloc(BOARD_SIZE * sizeof(BoardObject));

for (int i = 0; i < BOARD_SIZE; i++){
    board[i] = malloc(BOARD_SIZE * sizeof(BoardObject));
}
for (int i = 0; i < BOARD_SIZE; i++){
    for (int j = 0; j < BOARD_SIZE; j++){
        board[i][j] = NULL;
    }
}

I get an error that says I can't assign type *void to BoardObject. I am probably doing it wrong but couldn't seem to find any similar issues on stack exchange. If possible please explain the solution to me. Thank you!

Note: BoardObject is a struct.

Mocking
  • 1,764
  • 2
  • 20
  • 36
  • 2
    Is there a reason why you need such an obscure data type as a pointer-to-pointer lookup table? Why can't you use a real 2D array and [allocate it properly](http://stackoverflow.com/questions/12462615/how-do-i-correctly-set-up-access-and-free-a-multidimensional-array-in-c), instead of allocating segments all over the heap? – Lundin Apr 25 '14 at 11:31
  • 1
    `board = malloc(BOARD_SIZE * sizeof(BoardObject*));` – BLUEPIXY Apr 25 '14 at 11:32
  • @Lundin: I was actually uncertain as to how to approach this at all, this was what I came up with. I just want a 'simple' pointer to a 2d array that I can use like a regular array. Thanks for the link. – Mocking Apr 25 '14 at 11:44
  • @Mocking The simplest solution is almost always the best solution. And if you learn to use the VLA example posted by Jens in that link, you'll suddenly know more about dynamic 2D arrays than 99.9% of all C programmers on this site :) – Lundin Apr 25 '14 at 11:54
  • @Lundin: I tried doing it the way you linked me but it seems to be creating a pointer to a 2D array of objects, not a pointer to a 2d array of pointers to objects. – Mocking Apr 25 '14 at 11:57
  • @Lundin yep, including me. I had no idea that syntax worked until I followed your link. – JeremyP Apr 25 '14 at 11:57
  • 1
    @Mocking Indeed. Which is why I started by asking if you actually needed a pointer-to-pointer array (a.k.a. a lookup table). If that's what you need, then you actually need to add a few more stars: `double* (*A)[n] = malloc(sizeof(double*[n][n]));`. But start by defining what you need, _before_ attempting to find a solution for it. In other words, what's the actual problem you are trying to solve. – Lundin Apr 25 '14 at 12:02
  • @Lundin: I am trying to create a board that has objects that interact with each other. I am supposed to make the objects move around the board and do different things upon collision. I would check the board in a method outside of main for the object, move it, and if there are any other objects nearby interact with them, possibly removing the object that was there previously. Comparable to a game of pacman. – Mocking Apr 25 '14 at 12:11
  • 1
    Assuming that these objects are of a generic nature, it rather sounds like you need a 2D array of structs, where each struct contains one enum (type of object at this position) and one void pointer to the actual object (if needed). At any rate, you definitely want a true 2D array allocated in adjacent memory. – Lundin Apr 25 '14 at 12:48
  • @Lundin: Thanks for the help! I got it working now but I can't seem to figure out how to check if an array index is empty. board[i][j] == 0 and board[i][j] == NULL do not seem to be working. "Must have arithmetic or pointer type." – Mocking Apr 25 '14 at 12:58
  • Mocking do you understand that for a `struct`, it is not meaningful to write `== 0` or `= NULL` etc. on it? You can only operate on the struct's members in this way. – M.M Apr 25 '14 at 13:20
  • @Lundin, using pointer-to-VLA is kinda icky, IMHO. And VLA is not required to be supported any more in a C11 implementation. However if `BOARD_SIZE` is known at compile-time he certainly could use that technique. – M.M Apr 25 '14 at 13:23
  • @MattMcNabb Huh? The only VLA is the one inside sizeof(). The actual pointer is a standard array pointer. You can replace it with sizeof(X)*sizeof(Y) or whatever instead. Anyway, the important part is that you should only call malloc _once_, so that you get a chunk of _adjacently allocated_ memory bytes, so that you get a true multi-dimensional array, rather than some fragmented heap fiasco with pointers all over the place. – Lundin Apr 25 '14 at 13:29
  • `double (*A)[n]` is a pointer to VLA, this cannot be done in C89. I wouldn't call that a "true multi-dimensional array" either, it is still a 1-D array whose elements are other 1-D arrays. – M.M Apr 25 '14 at 13:35

4 Answers4

2

I can't seem to initiate all indexes of the array to null

Use calloc().

board = malloc(BOARD_SIZE * sizeof(BoardObject));

This code says "take a pointer-to-pointer and have it point to an array of BOARD_SIZE objects". That doesn't make any sense if you want to make an array of pointers. You should have allocated sizeof(BoardObject*).

JeremyP
  • 84,577
  • 15
  • 123
  • 161
Lundin
  • 195,001
  • 40
  • 254
  • 396
1

Make the following change

board = malloc(BOARD_SIZE * sizeof(BoardObject*));

Each board[i] is a pointer to BoardObject.

Why it is giving error?

Because, board[i][j] is of BoardObject type, not BoardObject *. NULL is defined as (void *)0. Hence, you cannot assign and pointer to BoardObject. Rather, use memset or calloc as explained by others.

doptimusprime
  • 9,115
  • 6
  • 52
  • 90
0

Just addressing what seems to be the main issue:

board[i][j] = NULL;

board[i][j] denotes a BoardObject. This code has the same problem as:

BoardObject b = NULL;

Hopefully it's clear why this doesn't work: a BoardObject is a struct, so the only thing you can initialize it with is a brace-enclosed initializer list, e.g.

BoardObject b = { 1, "foo", 5.5 };

Or if you have designed your object to work with all fields zero-initialized,

BoardObject b = { 0 };

Now, you can't specify a brace-enclosed initializer list to memory allocated via malloc. But you can copy structs by value:

BoardObject const b = { 0 };
// ...
board[i][j] = b;

This is more portable than using calloc, which sets all bits to zero. It's system-dependent whether floating-point types or pointer types have all-bits-zero as a valid representation of their zero value, although that is true for common modern systems so you would usually get away with it.

M.M
  • 138,810
  • 21
  • 208
  • 365
-1

malloc returns a void pointer which you must then cast to the required type. In this case...

board = (BoardObject**) malloc( ( BOARD_SIZE * BOARD_SIZE ) * sizeof(BoardObject ) );

The reason for this is that C has no concept of any base Object type, and therefore void is required for the return from malloc such that it may allocate memory of any type.

You also, as shown in your loops need to allocate more space to your board as demonstrated in my sample code above...

I hope this helps...

Rich Pickering
  • 237
  • 2
  • 6
  • 2
    A void* can be implicitly converted to any other pointer type. Do not cast the result of malloc for this reason. – Lundin Apr 25 '14 at 11:35
  • This is hopeless, `board` must point to pointers and you didn't malloc any pointers. – M.M Apr 25 '14 at 13:07
  • Yep, really the outer array should be allocated, and then the inner arrays allocated, and addresses linked between :). That's the way I'd do it if I were to write some C anyways. Though, in C, sometimes it's best to avoid 2D arrays, syntax can be complex to say the least... – Rich Pickering Apr 25 '14 at 13:10