2010-02-21 22 views
25

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 siendo la primera una serie 1D plana a lo largo de las líneas deMalloc una matriz tridimensional en C?

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

que perjudica a mi cerebro a utilizar. También he intentado una serie de una matriz 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; 
} 

que da una falla seg cuando intento para asignar los datos.

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

+1

añadir #include y retire el * de * array [i] y se ejecutará cuando se compila en gcc – Paul

+0

también soy curioso sobre cómo implementar esto. La solución de matriz 1D es "más limpia" (la que uso hasta ahora), sin embargo, para el cálculo numérico es significativamente más lenta que la 3D asignada estáticamente, debido al cálculo de "compensación". – lmount

+0

@WorldCitizeN ¿ha medido realmente este rendimiento? Cuando accede a una matriz estáticamente asignada, se ejecutan los mismos cálculos. La única diferencia es que no los escribes. – charliehorse55

Respuesta

16

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

Si el acceso al elemento correcto de la matriz está haciendo la cabeza adentro sin embargo, me gustaría escribir un método de utilidad para convertir x, y, z ubicaciones en un retranqueo en la matriz 1D

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

Gracias por la ayuda, creo que seguiré caminando penosamente, pero la sugerencia de tu función realmente me ayudó. – Mike

+0

Muy buen enfoque, aunque –

5

Oh qué odio la asignación gama malloc ^^

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

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

No es necesario que arroje el puntero devuelto por malloc. –

+1

Bien, solo estoy acostumbrado a hacerlo porque C++ arrojará errores si no lo hace. – AndiDog

7

No hay manera de C89 a hacer lo que deseas, porque un tipo de matriz en C sólo se puede especificar con valores conocidos en tiempo de compilación. Por lo tanto, para evitar la loca asignación dinámica, tendrá que apegarse a la forma unidimensional. Es posible utilizar 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 el C99 se puede utilizar una matriz de sintaxis ordinaria, 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, sólo funciona con las autoridades locales no estático matrices.

0

añadir #include "stdlib.h" y retire el * de * array [i] y se ejecutará cuando se compila en gcc 4.4.1 en Ubuntu

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

#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

printf() las declaraciones pueden ser útiles, pero no son un buen hábito para entrar. GDB realmente no es tan difícil de aprender y le ofrece impresión e introspección variable de forma gratuita, todo envuelto en un pequeño paquete. Y ni siquiera tiene que decidir dónde colocar el printf() s de antemano. – semisight

9

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 ya que parece estar interesado en saber cómo hacer frente a la malloc() de casos múltiples, he aquí un ejemplo:

En primer lugar, definir una función free_data(), lo que libera un int *** con xlen y ylen como los dos primeros tamaños de dimensión. No necesitamos un parámetro zlen al igual que 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 de bucles sobre el puntero data, se entera el puntero i º int **data[i]. Luego, para un puntero dado int **, lo recorre, descubriendo el j th int * en data[i][j], y lo libera. También necesita liberar data[i] una vez que haya liberado todos data[i][j], y finalmente, necesita liberar data sí mismo.

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 llamadas 1 + xlen + xlen*ylenmalloc, tenemos que ser capaces de gestionar 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 intentar asignarlos, de modo que si ocurre un error, podemos liberar todos los punteros.

Aparte de eso, la función es bastante simple. En primer lugar, asignar espacio para xlenint ** valores, a continuación, para cada uno de esos xlen punteros, asignamos espacio para ylenint * valores, y luego para cada uno de esos xlen*ylen punteros, asignamos espacio para zlenint valores, que nos da un espacio total para xlen*ylen*zlenint valores:

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 malloc llamadas un poco: en general, no se debe emitir el valor de retorno de malloc, y especificar el objeto que se está asignando para que el operando a sizeof operador vez de su tipo. Eso hace que malloc llame más fácil de escribir y menos propenso a errores. Debe incluir stdlib.h para malloc.

Aquí es un programa de prueba usando las dos funciones anteriores:

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

Por todos los medios utilizar 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.

+0

Hola, esto es probablemente muy antiguo, pero quería preguntar: ¿Es esto 'p = malloc (xlen * sizeof * p))' equivalente a 'p = (int **) malloc (xlen * sizeof (int *)); '? –

2

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

¿Estás seguro de que necesitas usar malloc? C permite la creación de matrices multidimensionales de forma nativa:

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

o puede utilizar 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 
+0

También creo que es posible emitir desde 'malloc', pero no estoy seguro, necesito verificar ... – Dims

+0

arrays multidimensionales nativos a menudo se topan con restricciones de memoria – christopherlovell

+0

@polyphant C asigna espacio exacto para los elementos, no hay datos adicionales – Dims

1
#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>; 

} 
0

Hope esto le 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í es su programa de

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; 

}

0

Esto debería funcionar, no se Encasillamiento el valor de retorno de 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; 
} 

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

1

Por debajo de la Código para asignaciones de memoria 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<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; 
1

Se está forzando a sí mismo 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 indirección acceder a los elementos reales, 2) el segundo método asigna los arrays 1D de nivel inferior independientemente.

¿Pero por qué insistes en asignar las matrices 1D de bajo nivel independientemente? No tienes que hacer eso. Y una vez que se toma en cuenta, usted debe darse cuenta de que hay un tercer método de construcción de la 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 nos fijamos en este método de asignación de cerca, usted debe ver que al final esto es más o menos la misma cosa como 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 repetitivas malloc. El ciclo subsiguiente simplemente distribuye esa memoria preasignada entre las sub-matrices (es decir, simplemente inicializa los punteros).

Sin embargo, si mira aún más cerca, también notará que la memoria real del elemento de matriz (int s que almacena los valores reales) se asigna exactamente de la misma forma que en su primer método: malloc(3 * 3 * 3 * sizeof(int)); - como una matriz plana plana contigua.

Ahora, si lo piensas bien, deberías 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 que nos gustaría calcular el índice en la marcha como

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

en el tercer método que pre-calcular los punteros a elementos de la matriz de antemano, utilizando esencialmente la misma fórmula, almacenar el pre -calculated resultados en matrices adicionales y recuperar posteriormente utilizando la sintaxis de matrices de acceso "natural"

array3d[x][y][x] 

la pregunta aquí es si este cálculo previo vale la pena el esfuerzo extra y la memoria adicional. 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 el segundo método valdría la pena considerar es cuando se trata de realmente irregular desigual gama /: una matriz multidimensional escasa, con algunas partes submatrices falta/no utilizado y de haber reducido tamaño. 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.

Cuestiones relacionadas