Подтвердить что ты не робот

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

Я переводя код MATLAB в C, а преобразование script, я использую 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?

4b9b3361

Ответ 1

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

Если доступ к правильному элементу массива все же делает вашу голову, я бы написал метод утилиты для преобразования x, y, z в смещение в 1D-массив

int offset(int x, int y, int z) { 
    return (z * xSize * ySize) + (y * xSize) + x; 
}

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

О, я ненавижу распределение массива 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;
}

Ответ 5

Вы уверены, что вам нужно использовать malloc? C позволяет создавать многомерные массивы изначально:

int a2[57][13][7];

Или вы можете использовать malloc следующим образом:

int (*a)[13][7]; // imitates 3d array with unset 3rd dimension
                 // actually it is a pointer to 2d arrays

a = malloc(57 * sizeof *a);    // allocates 57 rows

a[35][7][3] = 12; // accessing element is conventional

free(a); // freeing memory

Ответ 6

Таким образом вы можете выделить только один блок памяти, а динамический массив ведет себя как статический (т.е. тот же сопутствующий доступ к памяти). Вы также можете освободить память одним свободным (массивом), как обычные 1-D массивы.

double*** arr3dAlloc(const int ind1, const int ind2, const int ind3)
{
  int i;
  int j;
  double*** array = (double***) malloc( (ind1 * sizeof(double*)) + (ind1*ind2 * sizeof(double**)) + (ind1*ind2*ind3 * sizeof(double)) );
  for(i = 0; i < ind1; ++i) {
    array[i] = (double**)(array + ind1) + i * ind2;
    for(j = 0; j < ind2; ++j) {
      array[i][j] = (double*)(array + ind1 + ind1*ind2) + i*ind2*ind3 + j*ind3;
    }
  }
  return array;
}

Ответ 7

#include<stdio.h>
#include<stdlib.h>

#define MAXX 3
#define MAXY 4
#define MAXZ 5

main()
{
int ***p,i,j;
p=(int ***) malloc(MAXX * sizeof(int **));

for(i=0;i < MAXX;i++)
{
p[i]=(int **)malloc(MAXY * sizeof(int *));
for(j=0;j < MAXY;j++)
p[i][j]=(int *)malloc(MAXZ * sizeof(int));
}

for(k=0;k < MAXZ;k++)
for(i=0;i < MAXX;i++)
for(j=0;j < MAXY;j++)
p[i][j][k]= < something >;

}

Ответ 8

Ниже кода для выделения трехмерной памяти:

int row3d = 4;
int column3d = 4;
int height3d =4;
int val3d =10;

int ***arr3d = (int***)malloc (row3d*sizeof(int**));
for (int i =0 ; i<column3d;i++)
{
    arr3d[i] = (int**)malloc (column3d*sizeof(int*));
    for (int j = 0;j<height3d;j++)
    {
        arr3d[i][j] = (int*)malloc (height3d*sizeof(int));

        for (int z =0;z<height3d;z++,val3d++)
        {
            arr3d[i][j][z]   = val3d;
        }
    }

}
// De allocation.
for (int i=0;i<row3d;i++)
{
    for(int j=0;j<column3d;i++)
    {
        free(arr3d[i][j]);
    }
}
free(arr3d);
arr3d = 0;

Ответ 9

добавить #include "stdlib.h" и удалить массив * from * [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;
}

Ответ 10

Надеюсь, это поможет вам!!!!

При распределении памяти для 2D-массива внутри 3D-массива назначьте выделенную память массиву [i], а не * array [i], и это будет работать без seg-сбоев.

Вот ваша программа

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;

}

Ответ 11

Это должно работать, вы не приписываете возвращаемое значение malloc

#include <stdio.h>

int main () {
  int ***array = (int ***) malloc(3*sizeof(int**));
  int i, j;

  for (i = 0; i < 3; i++) {
    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;
  printf("%d\n", array[1][2][1]);
  return 0;
}

Рабочая ссылка: http://ideone.com/X2mcb8

Ответ 12

Вы заставляете себя воспринимать это как два принципиально разных способа выделения трехмерного массива. Это восприятие подкрепляется двумя окончательными дифференцирующими деталями: 1) второй метод использует несколько уровней косвенности для доступа к фактическим элементам; 2) второй метод самостоятельно выделяет 1D-массивы нижнего уровня.

Но почему именно вы настаиваете на том, чтобы независимо распределять массивы нижнего уровня 1D? Вам не обязательно это делать. И как только вы принимаете это во внимание, вы должны понимать, что существует третий метод построения 3D-массива

int ***array3d = malloc(3 * sizeof(int **));
int **array2d = malloc(3 * 3 * sizeof(int *));
int *array1d = malloc(3 * 3 * 3 * sizeof(int));

for (size_t i = 0; i < 3; i++) 
{
  array3d[i] = array2d + i * 3;
  for (size_t j = 0; j < 3; j++)
    array3d[i][j] = array1d + i * 3 * 3 + j * 3;
}

array[1][2][1] = 10;

Если вы внимательно посмотрите на этот метод распределения, вы должны увидеть, что в конце это почти то же самое, что и ваш второй метод: он создает трехуровневую структуру массива, используя промежуточные указатели на каждом уровне косвенности. Единственное различие заключается в том, что он заранее распределяет память для каждого уровня косвенности смежно, "одним выстрелом", заранее, вместо того, чтобы делать несколько повторяющихся вызовов malloc. Последующий цикл просто распределяет эту предварительно выделенную память среди подматриц (т.е. Просто инициализирует указатели).

Однако, если вы посмотрите еще ближе, вы также заметите, что фактическая память элемента массива (int, хранящая фактические значения) распределяется точно так же, как и в первом методе: malloc(3 * 3 * 3 * sizeof(int)); - как простой плоский непрерывный массив.

Теперь, если вы думаете об этом, вы должны понимать, что этот третий метод не сильно отличается от вашего первого. Они оба используют плоский массив размером xSize * ySize * zSize для хранения данных. Единственная реальная разница здесь - метод, который мы используем для расчета индекса для доступа к этим плоским данным. В первом методе мы рассчитываем индекс "на лету" как

array1d[z * ySize * xSize + y * xSize + x]

в третьем методе мы предварительно вычисляем указатели на элементы массива заранее, используя по существу ту же формулу, сохраняем предварительно рассчитанные результаты в дополнительных массивах и извлекаем их позже, используя синтаксис доступа к "естественному" массиву

array3d[x][y][x]

Вопрос в том, стоит ли этому предварительному расчету дополнительных усилий и дополнительной памяти. Ответ: вообще нет, это не так. Расходуя эту дополнительную память, вы не получите каких-либо заметных преимуществ в производительности (скорее всего, ваш код будет медленнее).

Единственная ситуация, когда ваш второй метод заслуживает внимания, - это когда вы имеете дело с подлинно зазубренным/оборванным массивом: редким многомерным массивом, в котором некоторые части вспомогательных массивов отсутствуют/не используются или имеют уменьшенный размер. Например, если известно, что некоторые поддиапазоны 1D или 2D вашего 3D-массива содержат только нули, вы можете не хранить их в памяти вообще и установить соответствующие указатели на нуль. Это означало бы использование вашего второго метода, где суб-массивы распределяются (или не распределяются) независимо. Если данные будут большими, экономия памяти может быть оправдана.

Также обратите внимание, что когда мы говорим о массивах с 3 и более измерениями, методы первого/второго/третьего распределения могут использоваться вместе одновременно для разных уровней косвенности. Вы можете решить реализовать 2D-массивы с использованием первого метода, а затем объединить их в 3D-массив, используя второй метод.