C offers no mechanism to pass the dimensions of variable-sized multidimensional arrays around. You have to implement it yourself, i.e. you have to come up with your own type that means int array[10][20]
. For example, in the (untested) code below, you can pass around an int_array *
, and use int_at
to get the value. You can also inspect the dimensions within int_array
, etc. The code using the int_array *
doesn't even need to know how many dimensions it has got.
vint_ptr_at
is doing, pretty much, what the compiler-generated code would do to compute the address, although that code can be optimized better than the generic version. Normally, if you were to iterate, you could use the int*
you got from int_ptr_at
, and keep incrementing it.
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct {
int *data;
int num_dims;
bool owns_data;
struct {
size_t count, product;
} dims[1];
} int_array;
void free_int_array(int_array *arr) {
if (arr && arr->owns_data) free(arr->data);
#ifdef DEBUG
arr->data = NULL;
#endif
free(arr);
}
static bool int_array_setup_dims(int_array *arr, va_list args) {
size_t n = 1;
int const num_dims = arr->num_dims;
for (int i = 0; i < num_dims; i++) {
int dim = va_arg(args, int);
arr->dims[i].count = dim;
arr->dims[num_dims - 1 - i].product = n;
if (n > SIZE_MAX / dim) return false;
n *= dim;
}
return true;
}
int_array *vnew_int_array_on(void *data, int num_dims, va_list args) {
int_array *arr = malloc(sizeof(int_array) + (num_dims-1)*sizeof(int));
if (!arr) goto fail;
arr->num_dims = num_dims;
if (!int_array_setup_dims(arr, args)) goto fail;
arr->data = data ? data : malloc(n * sizeof(int));
arr->num_dims = num_dims;
arr->owns_data = !data;
if (!arr->data) goto fail;
return arr;
fail:
free(arr);
return NULL;
}
int_array *new_int_array(int num_dims, ...) {
va_list args;
va_start(args, num_dims);
int_array *arr = vnew_int_array_on(NULL, num_dims, args);
va_end(args);
return arr;
}
int_array *new_int_array_on(void *data, int num_dims, ...) {
va_list args;
va_start(args, num_dims);
int_array *arr = vnew_int_array_in(data, num_dims, args);
va_end(args);
return arr;
}
int *vint_ptr_at(const int_array *arr, va_list args) {
size_t index = 0, n = 1;
int const num_dims = arr->num_dims;
for (int i = 0; i < num_dims; i++) {
int j = va_arg(args, int);
index += j * arr->dims[i].product;
}
return arr->data + index;
}
int *int_ptr_at(const int_array *arr, ...) {
va_list args;
va_start(args, arr);
int *ptr = vint_ptr_at(arr, args);
va_end(args);
return ptr;
}
int int_at(const int_array *arr, ...) {
va_list args;
va_start(args, arr);
int *ptr = vint_ptr_at((int_array*)arr, args);
va_end(args);
return *ptr;
}
size_t *indices_for(const int_array *arr) {
if (!arr) return NULL;
size_t const size = sizeof(size_t) * arr->num_dims;
size_t *indices = malloc(size);
if (indices) memset(indices, 0, size);
return indices;
}
bool next_index(const int_array *arr, size_t *indices) {
for (int i = arr->num_dims - 1; i >= 0; i--) {
if (indices[i] < arr->dims[i].count) {
indices[i] ++;
return true;
}
indices[i] = 0;
}
return false;
}
int main() {
int data[10][15];
int_array *arr = new_int_array_on(data, 2, 10, 15);
assert(arr->dims[0].dim == 10);
assert(arr->dims[1].dim == 15);
data[2][3] = 40;
data[4][5] = 50;
data[5][6] = 100;
assert(int_at(arr, 2, 3) == 40);
assert(int_at(arr, 4, 5) == 50);
assert(int_at(arr, 5, 6) == 100);
free_int_array(arr);
}
#include <stdio.h>
void iteration_example(const int_array *arr) {
size_t *indices = indices_for(arr);
if (indices) {
int *data = arr->data;
do {
printf("arr");
for (int i = 0; i < arr->num_dims; ++i)
printf("[%zd]", indices[i]);
printf(" = %d\n", *data++);
} while (next_index(arr, indices));
}
free(indices);
}