Как уже говорили другие, вероятно, лучше выделить один непрерывный кусок памяти, а затем самостоятельно определить индексирование. Вы можете написать функцию для этого, если хотите. Но так как вам, кажется, интересно знать, как обращаться с множественным случаем malloc()
, вот пример:
Сначала я определяю функцию free_data()
, которая освобождает int ***
с xlen
и ylen
в качестве первых двух размерных размеров. Нам не нужен параметр zlen
, так как free()
не принимает длину освобождаемого указателя.
void free_data(int ***data, size_t xlen, size_t ylen)
{
size_t i, j;
for (i=0; i < xlen; ++i) {
if (data[i] != NULL) {
for (j=0; j < ylen; ++j)
free(data[i][j]);
free(data[i]);
}
}
free(data);
}
Функция перебирает указатель data
, обнаруживает указатель i
th int **
data[i]
. Затем для данного указателя int **
он зацикливается на нем, обнаруживая j
th int *
в data[i][j]
, и освобождает его. Он также должен освободить data[i]
, как только освободит все data[i][j]
, и, наконец, он должен освободить data
сам.
Теперь к функции распределения. Функция немного усложняется проверкой ошибок. В частности, поскольку существует 1 + xlen + xlen*ylen
malloc
вызовов, мы должны быть в состоянии обработать сбой в любом из этих вызовов и освободить всю память, выделенную нами до сих пор. Чтобы упростить задачу, мы полагаемся на тот факт, что free(NULL)
не используется, поэтому мы устанавливаем все указатели на заданном уровне, равном NULL
, прежде чем пытаться их распределить, чтобы в случае возникновения ошибки мы могли освободить все указатели.
Кроме этого, функция достаточно проста. Сначала мы выделяем пространство для xlen
int **
значений, затем для каждого из этих xlen
указателей мы выделяем пространство для ylen
int *
значений, а затем для каждого из этих xlen*ylen
указателей мы выделяем пространство для zlen
int
значений, что дает нам общее пространство для xlen*ylen*zlen
int
значений:
int ***alloc_data(size_t xlen, size_t ylen, size_t zlen)
{
int ***p;
size_t i, j;
if ((p = malloc(xlen * sizeof *p)) == NULL) {
perror("malloc 1");
return NULL;
}
for (i=0; i < xlen; ++i)
p[i] = NULL;
for (i=0; i < xlen; ++i)
if ((p[i] = malloc(ylen * sizeof *p[i])) == NULL) {
perror("malloc 2");
free_data(p, xlen, ylen);
return NULL;
}
for (i=0; i < xlen; ++i)
for (j=0; j < ylen; ++j)
p[i][j] = NULL;
for (i=0; i < xlen; ++i)
for (j=0; j < ylen; ++j)
if ((p[i][j] = malloc(zlen * sizeof *p[i][j])) == NULL) {
perror("malloc 3");
free_data(p, xlen, ylen);
return NULL;
}
return p;
}
Обратите внимание, что я немного упростил вызовы malloc
: в общем случае вы не должны приводить возвращаемое значение malloc
, а вместо этого указывать объект, для которого вы выделяете, в качестве операнда для оператора sizeof
своего типа. Это делает malloc
звонков проще для написания и менее подвержено ошибкам. Вам нужно включить stdlib.h
для malloc
.
Вот тестовая программа, использующая две вышеуказанные функции:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
int ***data;
size_t xlen = 10;
size_t ylen = 100;
size_t zlen = 300;
size_t i, j, k;
srand((unsigned int)time(NULL));
if ((data = alloc_data(xlen, ylen, zlen)) == NULL)
return EXIT_FAILURE;
for (i=0; i < xlen; ++i)
for (j=0; j < ylen; ++j)
for (k=0; k < zlen; ++k)
data[i][j][k] = rand();
printf("%d\n", data[1][2][1]);
free_data(data, xlen, ylen);
return EXIT_SUCCESS;
}
Во что бы то ни стало, используйте этот подход, если вам будет проще его использовать. В целом, это будет медленнее, чем использование смежного куска памяти, но если вы обнаружите, что скорость вышеописанной схемы в порядке, и если она облегчает вашу жизнь, вы можете продолжать ее использовать. Даже если вы им не пользуетесь, приятно знать, как заставить работать такую схему.