Originally Posted by
qny
As laserlight suggested, I think it's way better to wrap the dynamic array in a struct and pass a pointer around.
I agree.
To help the OP, here is my favourite (modified to use ints), using C99 syntax. (I'll probably be scolded by Oogabooga and Adak for going overboard with this, but I believe this to be useful to the OP.)
Code:
struct table {
int rows;
int cols;
int cell[];
};
static inline int table_get(const struct table *const table, const int row, const int col)
{
if (table && row >= 0 && col >= 0 && row < table->rows && col < table->cols)
return table->cell[rows * table->cols + col];
else
return 0;
}
static inline int table_set(const struct table *const table, const int row, const int col, const int value)
{
if (table && row >= 0 && col >= 0 && row < table->rows && col < table->cols)
return table->cell[rows * table->cols + col] = value;
else
return 0;
}
struct table *table_free(struct table *const table)
{
if (table && table->rows > 0 && table->cols > 0) {
/* "Poison" the fields; makes easier to notice use-after-free bugs. */
table->rows = 0;
table->cols = 0;
free(table);
errno = 0;
} else
errno = EINVAL;
return NULL;
}
struct table *table_new(const int rows, const int cols)
{
const size_t cells = (size_t)rows * (size_t)cols;
const size_t bytes = cells * sizeof (int);
const size_t total = bytes + sizeof (struct table);
struct table *table;
/* Check for valid size. Uses C99 casting rules
* to detect overflow. */
if (rows < 1 || cols < 1 ||
(size_t)(cells / rows) != (size_t)(cols) ||
(size_t)(bytes / sizeof (int)) != cells ||
total < bytes) {
errno = EINVAL;
return NULL;
}
table = malloc(total);
if (!table) {
errno = ENOMEM;
return NULL;
}
table->rows = rows;
table->cols = cols;
return table;
}
#define ROWS(table) ((table)->rows)
#define COLS(table) ((table)->cols)
#define CELL(table, row, col) ((table)->cell[ (row) * ((table)->cols) + (col) ])
/* Access to row r, column c is
* table->cell[ r * table->cols + c ]
* The data is in row-major order, the same as in 2D arrays in C.
*/
and here is an example of how to create a 20-row, 30-column array and fill it with (col + row), with upper left corner (row 0, col 0) zero:
Code:
struct table *t;
int r, c;
t = table_new(20, 30);
if (!t) {
fprintf(stderr, "Not enough memory.\n");
exit(1);
}
/* Fill in the table with row+column values */
for (r = 0; r < ROWS(t); r++)
for (c = 0; c < COLS(t); c++)
CELL(t, r, c) = r + c;
/* Print the table contents. */
for (r = 0; r < ROWS(t); r++) {
for (c = 0; c < COLS(t); c++)
printf("%3d", CELL(t, r, c));
printf("\n");
}
/* Since t is no longer used, discard it. */
t = table_free(t);
When working with 2D tables, the edges are often a headache. Instead of CELL(table,row,column)=value you can use table_set(table,row,column,value) which checks its parameters and does nothing if row,column is outside the table. The same goes for table_get(). Both functions return 0 if outside the table.