Malloc una matriz tridimensional en C?

Estoy traduciendo algunos códigos MATLAB en C y el script que estoy convirtiendo hace un uso intensivo de matrices 3D con 10 * 100 * 300 entradas complejas. El tamaño de la matriz también depende de la entrada del sensor, idealmente la matriz se debe asignar dinámicamente. Hasta ahora he intentado dos enfoques, el primero es una matriz 1D plana a lo largo de las líneas de

value = array[x + (y*xSize) + (z*ySize*xSize)] 

Lo cual duele mi cerebro para usar. También probé una serie de punteros

 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; } 

Lo cual da una falla seg cuando bash asignar datos.

En un mundo perfecto, me gustaría usar el segundo método con la notación de matriz para una progtwigción más limpia y fácil. ¿Hay una mejor manera de asignar dinámicamente una matriz tridimensional en C?

Me gustaría ir por la primera opción (la única matriz 1D), ya que le dará un solo bloque de memoria para jugar en lugar de potencialmente miles de bloques de memoria fragmentados

Sin embargo, si accediendo al elemento correcto de la matriz está entrando, escribiría un método de utilidad para convertir las ubicaciones x, y, z en una compensación en la matriz 1D.

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

Como han dicho otros, probablemente sea mejor asignar un trozo contiguo de memoria y luego averiguar la indexación usted mismo. Puede escribir una función para hacerlo si lo desea. Pero dado que parece estar interesado en saber cómo lidiar con el caso múltiple de malloc() , aquí hay un ejemplo:

Primero, defino una función free_data() , que libera un int *** con xlen y ylen como los dos primeros tamaños de dimensión. No necesitamos un parámetro zlen como free() no toma la longitud del puntero que se libera.

 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); } 

La función realiza un bucle sobre los data del puntero, descubre los data del indicador i th int ** data[i] . Luego, para un puntero int ** dado, pasa por encima de él, detecta la j th int * en data[i][j] y lo libera. También necesita liberar data[i] una vez que haya liberado todos los data[i][j] y, finalmente, necesite liberar los data .

Ahora a la función de asignación. La función es un poco complicada por la comprobación de errores. En particular, dado que hay 1 + xlen + xlen*ylen malloc llamadas, tenemos que ser capaces de manejar un error en cualquiera de esas llamadas, y liberar toda la memoria que hemos asignado hasta el momento. Para facilitar las cosas, confiamos en el hecho de que free(NULL) no es operativo, por lo que establecemos todos los punteros en un nivel dado igual a NULL antes de tratar de asignarlos, de modo que si ocurre un error, podemos liberar todos los punteros.

Aparte de eso, la función es lo suficientemente simple. Primero xlen espacio para xlen int ** values, luego para cada uno de esos xlen punteros, xlen espacio para ylen int * values, y luego para cada uno de esos xlen*ylen punteros, zlen espacio para zlen int values, dándonos un espacio total para xlen*ylen*zlen int values:

 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; } 

Tenga en cuenta que he simplificado un poco las llamadas malloc : en general, no debe convertir el valor devuelto de malloc , y especifique el objeto que está asignando como operando a operador sizeof lugar de su tipo. Eso hace que las llamadas malloc sean más simples de escribir y menos propensas a errores. Necesita incluir stdlib.h para malloc .

Aquí hay un progtwig de prueba que usa las dos funciones anteriores:

 #include  #include  #include  #include  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; } 

Por supuesto, use este enfoque si le resulta más fácil usarlo. En general, esto será más lento que usar un trozo contiguo de memoria, pero si encuentra que la velocidad está bien con el esquema anterior, y si le hace la vida más fácil, puede seguir usándolo. Incluso si no lo usa, es bueno saber cómo hacer que ese esquema funcione.

¿Estás seguro de que necesitas utilizar malloc ? C permite crear matrices multidimensionales de forma nativa:

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

O puede usar malloc de la siguiente manera:

 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 

No es posible en C89 hacer lo que desee, ya que un tipo de matriz en C solo se puede especificar con valores conocidos de tiempo de comstackción. Por lo tanto, para evitar la loca asignación dinámica, tendrá que apegarse a la forma unidimensional. Puede usar una función para facilitar este proceso

 int index(int x, int y, int z) { return x + (y*xSize) + (z*ySize*xSize); } int value = array[index(a, b, c)]; 

En C99 puede usar una syntax de matriz común incluso si las dimensiones son valores de tiempo de ejecución:

 int (*array)[X][Y][Z] = (int(*)[X][Y][Z])malloc(sizeof *p); // fill... int value = (*array)[a][b][c]; 

Sin embargo, solo funciona con arreglos locales no estáticos.

Oh, odio la asignación de array malloc ^^

Aquí hay una versión correcta, básicamente era solo una línea incorrecta:

 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; } 

De esta forma, puede asignar solo 1 bloque de memoria y la matriz dinámica se comporta como la estática (es decir, la misma contigüidad de memoria). También puede liberar memoria con una única libre (matriz) como matrices ordinarias en 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; } 

Acerca de segfault, estoy bastante seguro de que alguien más lo señaló, pero por las dudas, hay un ‘*’ extra en la primera línea del primer bucle for

 for (i = 0; i < 3; i++) { *array[i] = malloc(3*sizeof(int*)); // ^ we dont want to deference array twice for (j = 0; j < 3; j++) { array[i][j] = malloc(3*sizeof(int)); } } 

intente lo siguiente:

  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)); } } 

¡¡¡¡Espero que esto te ayudará!!!!

Al asignar memoria para una matriz 2D dentro de la matriz 3D, asigne la memoria asignada a la matriz [i] y no * matriz [i] y esto funcionará sin fallo por seg.

Aquí está tu progtwig

 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; 

}

Debajo del Código para asignaciones de memoria en 3D:

 int row3d = 4; int column3d = 4; int height3d =4; int val3d =10; int ***arr3d = (int***)malloc (row3d*sizeof(int**)); for (int i =0 ; i 

Te estás forzando a percibir esto como dos formas fundamentalmente diferentes de asignar una matriz 3D. Esta percepción se ve reforzada por dos detalles diferenciadores definitivos: 1) el segundo método utiliza varios niveles de direccionamiento indirecto para acceder a los elementos reales, 2) el segundo método asigna las matrices de 1D de bajo nivel de forma independiente .

Pero, ¿por qué insistes en asignar las matrices 1D de nivel inferior de forma independiente ? No tienes que hacer eso. Y una vez que lo tengas en cuenta, deberías darte cuenta de que hay un tercer método para construir tu matriz 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; 

Si observa este método de asignación de cerca, debería ver que al final esto es más o menos lo mismo que su segundo método: construye una estructura de matriz de tres niveles mediante el uso de punteros intermedios en cada nivel de indirección. La única diferencia es que preasigna la memoria para cada nivel de direccionamiento indirecto, "de una vez", de antemano, en lugar de realizar múltiples llamadas malloc repetitivas. El ciclo subsiguiente simplemente distribuye esa memoria preasignada entre las sub-matrices (es decir, simplemente inicializa los punteros).

Sin embargo, si observas aún más de cerca, también notarás que la memoria real del elemento de matriz (los int s que almacenan los valores reales) se asignan exactamente de la misma manera que lo harían en tu primer método: malloc(3 * 3 * 3 * sizeof(int)); - como una matriz plana plana contigua.

Ahora, si lo piensas bien, debes darte cuenta de que este tercer método no es muy diferente al primero. Ambos usan una matriz plana de tamaño xSize * ySize * zSize para almacenar los datos. La única diferencia real aquí es el método que usamos para calcular el índice para acceder a esos datos planos. En el primer método, calcularíamos el índice sobre la marcha como

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

en el tercer método precalculamos los indicadores a los elementos de la matriz por adelantado , usando esencialmente la misma fórmula, almacenamos los resultados precalculados en matrices adicionales y los recuperamos más tarde usando la syntax de acceso a la matriz "natural"

 array3d[x][y][x] 

La pregunta aquí es si este precalculo vale la pena el esfuerzo extra y la memoria extra. La respuesta es: generalmente no, no lo es. Al gastar esta memoria extra, no obtendrás ningún beneficio de rendimiento apreciable (es probable que tu código disminuya).

La única situación en la que podría valer la pena considerar el segundo método es cuando se trata de una matriz fragmentada / desigual : una matriz multidimensional dispersa con algunas subcajas que faltan / no se usan o que tienen un tamaño reducido. Por ejemplo, si se sabe que algunas sub-matrices 1D o 2D de su matriz 3D contienen solo ceros, puede optar por no almacenarlas en la memoria y establecer los punteros correspondientes como nulos. Esto implicaría usar su segundo método, donde las sub-matrices se asignan (o no se asignan) de forma independiente. Si los datos son grandes, los ahorros de memoria resultantes podrían valer la pena.

También tenga en cuenta que cuando hablamos de matrices con 3 y más dimensiones, los métodos de asignación primero / segundo / tercero se pueden usar juntos, simultáneamente para diferentes niveles de direccionamiento indirecto. Puede decidir implementar matrices 2D usando el primer método y luego combinarlas en una matriz 3D usando el segundo método.

agregue #include “stdlib.h” y elimine el * de * array [i] y se ejecutará cuando se compile en gcc 4.4.1 en Ubuntu

también si agrega declaraciones de impresión puede encontrar sus errores más rápido

 #include  #include  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; } 
 #include #include #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 >; } 

Esto debería funcionar, no está encasillando el valor de retorno de malloc

 #include  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; } 

Enlace de trabajo: http://ideone.com/X2mcb8