Вот модифицированная версия решения quinmars , которое выделяет только один блок памяти и может использоваться с общими значениями благодаря void *
:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
void ** array2d(size_t rows, size_t cols, size_t value_size)
{
size_t index_size = sizeof(void *) * rows;
size_t store_size = value_size * rows * cols;
char * a = malloc(index_size + store_size);
if(!a) return NULL;
memset(a + index_size, 0, store_size);
for(size_t i = 0; i < rows; ++i)
((void **)a)[i] = a + index_size + i * cols * value_size;
return (void **)a;
}
int printf(const char *, ...);
int main()
{
int ** a = (int **)array2d(5, 5, sizeof(int));
assert(a);
a[4][3] = 42;
printf("%i\n", a[4][3]);
free(a);
return 0;
}
Я не уверен, действительно ли безопасно приводить void **
к int **
(я думаю, стандарт допускает преобразования при преобразовании в / из void *
?), Но он работает в gcc. Чтобы быть в безопасности, вы должны заменить каждый случай void *
на int *
...
Следующие макросы реализуют типобезопасную версию предыдущего алгоритма:
#define alloc_array2d(TYPE, ROWS, COLS) \
calloc(sizeof(TYPE *) * ROWS + sizeof(TYPE) * ROWS * COLS, 1)
#define init_array2d(ARRAY, TYPE, ROWS, COLS) \
do { for(int i = 0; i < ROWS; ++i) \
ARRAY[i] = (TYPE *)(((char *)ARRAY) + sizeof(TYPE *) * ROWS + \
i * COLS * sizeof(TYPE)); } while(0)
Используйте их так:
int ** a = alloc_array2d(int, 5, 5);
init_array2d(a, int, 5, 5);
a[4][3] = 42;