Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I dynamically allocate a 2D array of structs?

The dimensions are unknown at compile time, so I'm trying to allocate a 2D array of structs dynamically. The code compiles but I get a bad access when accessing an element.

// The struct
typedef struct
{
    NSInteger numActors;
    Actor *a1;
    Actor *a2;
    Actor *a3;
    Actor *a4;
    Actor *a5;
} GridNode;

// In interface
GridNode **grid;

// In init
NSInteger nx = inFrame.size.width / blockSize;
NSInteger ny = inFrame.size.height / blockSize;
grid = malloc(sizeof(GridNode) * nx * ny);
grid[10][20].numActors = 3; // EXC_BAD_ACCESS
like image 848
Morrowless Avatar asked Jun 22 '10 14:06

Morrowless


3 Answers

C has only 1D arrays, so 2D ones can be defined in two ways:

  1. As an array of arrays, like GridNode **grid, so to be accessed by grid[x][y], but then you have to init each row separately (right, as yehnan managed to anwser first):

    grid=malloc(sizeof(GridNode*)*nx);  
    for(int e=0;e<nx;e++) grid[e]=malloc(sizeof(GridNode)*ny);
    
  2. As a 1D array with tricky indexing:

    grid=malloc(sizeof(GridNode)*nx*ny);  
    grid[(10-1)*nx+20] //grid[10,20]
    
like image 192
mbq Avatar answered Sep 30 '22 05:09

mbq


Roughly the code should look like:

grid = (GridNode **) malloc(sizeof(GridNode *) * nx);
int i;
for(i = 0; i < nx; i++) {
    grid[i] = (GridNode *) malloc(sizeof(GridNode) * ny);
}

And remember to free them.

like image 21
yehnan Avatar answered Sep 30 '22 05:09

yehnan


A simpler alternative would be to create a single-dimension NSArray or NSMutableArray and use a little math to access the correct row and column:

NSUInteger width  = 10;
NSUInteger height = 10;
NSUInteger size   = width * height;
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:size];

To insert a GridNode at row 5, column 3, you could do this:

NSUInteger index = (row - 1) * width + col;
[array insertObject:myNode atIndex:index];

To retrieve a node from row 2 column 6 you would do:

NSUInteger index = (row - 1) * width + col;
[array objectAtIndex:index];

The memory in your computer is of a single dimension. The multi-dimensional addressing that we're all familiar with from C is really just syntactic sugar that performs a similar operation as I've shown above.

The only caveat I can think of is that you will probably have to convert GridNode from a C structure to an Objective-C class for this to work.

like image 39
Paul Avatar answered Sep 30 '22 05:09

Paul