C 没有提供传递可变大小多维数组维度的机制。你必须自己实现它,即你必须想出你自己的类型,这意味着int array[10][20]。例如,在下面的(未经测试的)代码中,您可以传递一个int_array *,并使用int_at 来获取值。您还可以检查int_array等内部的维度。使用int_array *的代码甚至不需要知道它有多少维度。
vint_ptr_at 所做的几乎就是编译器生成的代码在计算地址时所做的事情,尽管该代码可以比通用版本更好地优化。通常,如果要进行迭代,可以使用从 int_ptr_at 获得的 int*,并不断增加它。
#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);
}