Step 7 of 9
A 2D grid on the heap
When a grid's size is only known at runtime, a common layout is an array of row pointers, each row its own allocation:
grid ──> [ row0 ] ──> [ 0 0 0 0 ]
[ row1 ] ──> [ 0 0 0 0 ]
[ row2 ] ──> [ 0 0 0 0 ]
int **g = malloc(rows * sizeof *g); // the row pointers
for (int r = 0; r < rows; r++)
g[r] = calloc(cols, sizeof *g[r]); // each row, zeroed
sizeof *g means "the size of whatever g points at". It stays correct even if you change the type later, which is why many style guides prefer it.
Freeing goes in reverse: every row first, then the array of pointers. If any allocation fails halfway, free what you already allocated and return NULL.
Your turn: write make_grid (all cells zero) and free_grid.