Malloc uma matriz tridimensional em C?

Estou traduzindo algum código do MATLAB para C e o script que estou convertendo faz uso pesado de matrizes 3D com inputs complexas de 10 * 100 * 300. O tamanho da matriz também depende da input do sensor, idealmente a matriz deve ser alocada dinamicamente. Até agora eu tentei duas abordagens sendo a primeira uma matriz 1D plana ao longo das linhas de

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

Que dói meu cérebro para usar. Eu também tentei uma matriz de uma matriz de pointers

 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 dá uma falha seg quando tento atribuir dados.

Em um mundo perfeito, eu gostaria de usar o segundo método com a notação de matriz para uma programação mais limpa e fácil. Existe uma maneira melhor de alocar dinamicamente uma matriz tridimensional em C?

Eu escolheria a primeira opção (o array 1D único), pois ele fornecerá um único bloco de memory para ser executado em vez de potencialmente milhares de blocos de memory fragmentados.

Se o access ao elemento correto da matriz estiver funcionando, eu escreveria um método utilitário para converter locais x, y, z em um deslocamento para a matriz 1D

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

Como outros já disseram, é provavelmente melhor alocar um pedaço contíguo de memory e depois descobrir a indexação por conta própria. Você pode escrever uma function para fazer isso, se quiser. Mas desde que você parece estar interessado em saber como lidar com o caso de múltiplos malloc() , aqui está um exemplo:

Primeiro, defino uma function free_data() , que libera um int *** com xlen e ylen como os dois primeiros tamanhos de dimensão. Nós não precisamos de um parâmetro zlen assim como free() não leva o comprimento do ponteiro sendo liberado.

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

A function faz um loop sobre os data do ponteiro, descobre os data do i int ** pointer data[i] . Então, para um determinado int ** pointer, ele faz um loop sobre ele, localizando o j th int * em data[i][j] , e o libera. Ele também precisa liberar os data[i] depois de liberar todos os data[i][j] e, finalmente, precisar liberar os data si.

Agora para a function de alocação. A function é um pouco complicada pela verificação de erros. Em particular, uma vez que existem 1 + xlen + xlen*ylen malloc , temos que ser capazes de lidar com uma falha em qualquer uma dessas chamadas e liberar toda a memory que alocamos até agora. Para tornar as coisas mais fáceis, nós confiamos no fato de que free(NULL) não é op, então nós definimos todos os pointers em um nível igual a NULL antes de tentarmos alocá-los, então se um erro acontecer, podemos liberar todos os pointers.

Fora isso, a function é bastante simples. Primeiro alocamos espaço para valores xlen int ** , então para cada um desses pointers xlen , alocamos espaço para valores ylen int * e, em seguida, para cada um desses pointers xlen*ylen , alocamos espaço para valores zlen int , dando-nos um espaço total para xlen*ylen*zlen de 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; } 

Note que simplifiquei bastante as chamadas malloc : em geral, você não deve converter o valor de retorno de malloc e especificar o object para o qual você está alocando como operando para sizeof operator em vez de seu tipo. Isso torna as chamadas malloc mais simples de escrever e menos propensas a erros. Você precisa include stdlib.h para malloc .

Aqui está um programa de teste usando as duas funções acima:

 #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 todos os meios, use essa abordagem se achar mais fácil usá-la. Em geral, isso será mais lento do que usar um pedaço contíguo de memory, mas se você achar que a velocidade está boa com o esquema acima, e se facilitar a sua vida, você pode continuar usando-a. Mesmo se você não usá-lo, é bom saber como fazer esse esquema funcionar.

Tem certeza de que precisa usar o malloc ? C permite a criação de matrizes multidimensionais de forma nativa:

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

Ou você pode usar o malloc da seguinte maneira:

 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 

Não há nenhuma maneira em C89 para fazer o que você deseja, porque um tipo de matriz em C só pode ser especificado com valores conhecidos de tempo de compilation. Então, para evitar a alocação dinâmica, você terá que se ater ao modo unidimensional. Você pode usar uma function para facilitar esse processo

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

Em C99, você pode usar uma syntax de matriz comum, mesmo se as dimensões forem valores de tempo de execução:

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

No entanto, ele só funciona com matrizes não estáticas locais.

Oh, eu odeio alocação de array malloc ^^

Aqui está uma versão correta, basicamente, era apenas uma linha incorreta:

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

Dessa forma, você pode alocar apenas um bloco de memory e o array dynamic se comporta como o estático (ou seja, a mesma contiguidade de memory). Você também pode liberar memory com um único array (matricial) livre como ordinário 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; } 

Sobre o segfault, eu tenho certeza que alguém mais apontou isso, mas apenas no caso, há um extra ‘*’ na primeira linha do primeiro loop 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)); } } 

tente o seguinte:

  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 isso ajude você !!!!

Ao alocar a memory para o array 2D dentro do array 3D, atribua a memory alocada ao array [i] e não ao * array [i] e isso funcionará sem falha seg.

Aqui é o seu programa

 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; 

}

Abaixo do Código para alocações de memory 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 

Você está se forçando a perceber isso como duas formas fundamentalmente diferentes de alocar um array 3D. Esta percepção é reforçada por dois detalhes de diferenciação definitivos: 1) o segundo método usa vários níveis de indireção para acessar os elementos reais, 2) o segundo método aloca os arrays 1D de nível inferior independentemente .

Mas por que exatamente você insiste em alocar os arrays 1D de nível inferior independentemente ? Você não precisa fazer isso. E depois de levar isso em conta, você deve perceber que há um terceiro método para construir seu array 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; 

Se você olhar este método de alocação de perto, você verá que no final isso é praticamente a mesma coisa que o seu segundo método: ele constrói uma estrutura de array de três níveis usando pointers intermediários em cada nível de indireção. A única diferença é que pré-aloca memory para cada nível de indireção contígua, "de uma só vez", em vez de fazer múltiplas chamadas repetitivas de malloc . O ciclo subseqüente simplesmente distribui a memory pré-alocada entre os sub-arrays (isto é, simplesmente inicializa os pointers).

No entanto, se você olhar ainda mais de perto, você também notará que a memory do elemento da matriz real (os int s que armazenam os valores reais) são alocados exatamente da mesma maneira que no primeiro método: malloc(3 * 3 * 3 * sizeof(int)); - como uma matriz plana plana e contígua.

Agora, se você pensar sobre isso, você deve perceber que este terceiro método não é muito diferente do seu primeiro. Ambos usam uma matriz plana de tamanho xSize * ySize * zSize para armazenar os dados. A única diferença real aqui é o método que usamos para calcular o índice para acessar esses dados planos. No primeiro método, calcularíamos o índice on-the-fly

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

no terceiro método, pré-calculamos os pointers para os elementos do array antecipadamente , usando essencialmente a mesma fórmula, armazenamos os resultados pré-calculados em matrizes adicionais e os recuperamos mais tarde usando a syntax de access do array "natural".

 array3d[x][y][x] 

A questão aqui é se esse pré-cálculo vale o esforço extra e a memory extra. A resposta é: geralmente não, não é. Ao gastar essa memory extra, você não obterá nenhum benefício apreciável de desempenho (as chances são de que seu código fique mais lento).

A única situação em que seu segundo método pode valer a pena ser considerado é quando você está lidando com um array realmente irregular : um array multidimensional esparso com algumas partes de sub-arrays faltando / sem uso ou com tamanho reduzido. Por exemplo, se alguns subarrays 1D ou 2D de seu array 3D forem conhecidos por conterem apenas zeros, você pode decidir não armazená-los na memory e definir os pointers correspondentes como null. Isso implicaria em usar seu segundo método, onde as sub-matrizes são alocadas (ou não alocadas) independentemente. Se os dados forem grandes, a economia de memory resultante pode valer a pena.

Observe também que quando estamos falando de arrays com 3 e mais dimensões, o primeiro / segundo / terceiro methods de alocação podem ser usados ​​juntos, simultaneamente para diferentes níveis de indireção. Você pode decidir implementar arrays 2D usando o primeiro método e depois combiná-los em um array 3D usando o segundo método.

adicione #include “stdlib.h” e remova o * de * array [i] e ele será executado quando compilado no gcc 4.4.1 no Ubuntu

Além disso, se você adicionar instruções de impressão, poderá encontrar seus bugs mais rapidamente

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

Isso deve funcionar, você não está typecasting o 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; } 

Link de trabalho: http://ideone.com/X2mcb8