Malloc 3-мерный массив в C?

Я перевожу некоторый код 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?

28
задан Brian 21 July 2014 в 21:23
поделиться

5 ответов

добавьте #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;
}
0
ответ дан 28 November 2019 в 03:08
поделиться

Я бы выбрал первый вариант (единый одномерный массив), поскольку это даст вам единый блок памяти для игры, а не потенциально тысячи фрагментированных блоков памяти

Если доступ к нужному элементу массива не дает вам покоя, я бы написал метод утилиты для преобразования местоположений x, y, z в смещение в одномерном массиве

int offset(int x, int y, int z) { 
    return (z * xSize * ySize) + (y * xSize) + x; 
}
18
ответ дан 28 November 2019 в 03:08
поделиться

Как говорили другие, вероятно, лучше выделить один непрерывный кусок памяти и затем разберитесь с индексацией самостоятельно. Вы можете написать функцию для этого, если хотите. Но поскольку вам, кажется, интересно узнать, как справиться с множественным случаем 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;
}

Обязательно используйте этот подход, если вам легче его использовать. В общем, это будет медленнее, чем использование непрерывного фрагмента памяти, но если вы обнаружите, что скорость в порядке с приведенной выше схемой, и если это облегчит вашу жизнь, вы можете продолжать ее использовать. Даже если вы им не пользуетесь, приятно знать, как заставить такую ​​схему работать.

9
ответ дан 28 November 2019 в 03:08
поделиться

В 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];

Однако это работает только с локальными нестатическими массивами.

7
ответ дан 28 November 2019 в 03:08
поделиться

Как же я ненавижу выделение массивов 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;
}
6
ответ дан 28 November 2019 в 03:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: