Я перевожу некоторый код MATLAB в C и сценарий, который я преобразовываю, делает интенсивное использование 3D массивов с 10*100*300 сложными записями. Размер массива также зависит от входа датчика, идеально массив должен быть выделен динамично. До сих пор я попробовал два подхода первое, являющееся плоским 1D массив вроде
value = array[x + (y*xSize) + (z*ySize*xSize)]
Который повреждает мой мозг для использования. Я также попробовал массив массива указателей
int main () {
int ***array = malloc(3*sizeof(int**));
int i, j;
for (i = 0; i < 3; i++) {
*array[i] = malloc(3*sizeof(int*));
for (j = 0; j < 3; j++) {
array[i][j] = malloc(3*sizeof(int));
}
}
array[1][2][1] = 10;
return 0;
}
Который дает отказ seg, когда я пытаюсь присвоить данные.
В идеальном мире я хотел бы использовать второй метод с нотацией массива для инструмента для очистки, более легкого программирования. Существует ли лучший способ динамично выделить трехмерную антенную решетку в C?
добавьте #include "stdlib.h" и уберите * из *array[i] и он будет работать при компиляции в gcc 4.4.1 на Ubuntu
также если вы добавите операторы печати, вы сможете быстрее найти ошибки
#include <stdio.h>
#include <stdlib.h>
int main () {
int ***array = malloc(3*sizeof(int**));
int i, j;
printf("%s\n","OK");
for (i = 0; i < 3; i++) {
printf("i = %i \n",i);
array[i] = malloc(3*sizeof(int*));
for (j = 0; j < 3; j++) {
printf("i,j = %i,%i \n",i,j);
array[i][j] = malloc(3*sizeof(int));
}
}
array[1][2][1] = 10;
return 0;
}
Я бы выбрал первый вариант (единый одномерный массив), поскольку это даст вам единый блок памяти для игры, а не потенциально тысячи фрагментированных блоков памяти
Если доступ к нужному элементу массива не дает вам покоя, я бы написал метод утилиты для преобразования местоположений x, y, z в смещение в одномерном массиве
int offset(int x, int y, int z) {
return (z * xSize * ySize) + (y * xSize) + x;
}
Как говорили другие, вероятно, лучше выделить один непрерывный кусок памяти и затем разберитесь с индексацией самостоятельно. Вы можете написать функцию для этого, если хотите. Но поскольку вам, кажется, интересно узнать, как справиться с множественным случаем 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 ]
и освобождает его. Ему также необходимо освободить данных [i]
после того, как он освободит все данные [i] [j]
, и, наконец, он должен освободить сами данные
.
Теперь о функции распределения. Функция немного усложняется проверкой ошибок.В частности, поскольку существуют вызовы 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;
}
Обязательно используйте этот подход, если вам легче его использовать. В общем, это будет медленнее, чем использование непрерывного фрагмента памяти, но если вы обнаружите, что скорость в порядке с приведенной выше схемой, и если это облегчит вашу жизнь, вы можете продолжать ее использовать. Даже если вы им не пользуетесь, приятно знать, как заставить такую схему работать.
В C89 нет возможности сделать то, что вы хотите, потому что тип массива в C может быть задан только известными во время компиляции значениями. Поэтому, чтобы избежать безумного динамического распределения, вам придется придерживаться одномерного способа. Вы можете использовать функцию, чтобы облегчить этот процесс
int index(int x, int y, int z) {
return x + (y*xSize) + (z*ySize*xSize);
}
int value = array[index(a, b, c)];
В C99 вы можете использовать обычный синтаксис массива, даже если размерность является значением времени выполнения:
int (*array)[X][Y][Z] = (int(*)[X][Y][Z])malloc(sizeof *p);
// fill...
int value = (*array)[a][b][c];
Однако это работает только с локальными нестатическими массивами.
Как же я ненавижу выделение массивов malloc ^^
Вот правильная версия, в основном это была всего одна неправильная строка:
int main () {
int ***array = (int***)malloc(3*sizeof(int**));
int i, j;
for (i = 0; i < 3; i++) {
// Assign to array[i], not *array[i] (that would dereference an uninitialized pointer)
array[i] = (int**)malloc(3*sizeof(int*));
for (j = 0; j < 3; j++) {
array[i][j] = (int*)malloc(3*sizeof(int));
}
}
array[1][2][1] = 10;
return 0;
}