programacion - ¿Cómo puedo configurar, acceder y liberar correctamente una matriz multidimensional en C?
matriz de caracteres en c (4)
He visto docenas de preguntas sobre "qué pasa con mi código" con respecto a las matrices multidimensionales en C. Por alguna razón, las personas no parecen entender lo que está sucediendo aquí, así que decidí responder esta pregunta como una referencia a los demás :
¿Cómo puedo configurar, acceder y liberar correctamente una matriz multidimensional en C?
Si otros tienen consejos útiles, ¡no dude en publicarlos!
Hay al menos cuatro formas diferentes de crear o simular una matriz multidimensional en C89.
Uno es "asignar cada fila por separado", descrito por Mike en su respuesta. No es una matriz multidimensional, simplemente imita una (en particular, imita la sintaxis para acceder a un elemento). Puede ser útil en el caso en que cada fila tenga un tamaño diferente, por lo que no está representando una matriz, sino algo con un "borde irregular".
Uno es "asignar una matriz multidimensional". Se ve así:
int (*rows)[NUM_ROWS][NUM_COLS] = malloc(sizeof *rows);
...
free(rows);
Entonces la sintaxis para acceder al elemento [i, j] es (*rows)[i][j]
. En C89, se deben conocer NUM_COLS
y NUM_ROWS
en tiempo de compilación. Esta es una verdadera matriz 2-D, y las rows
son un puntero a ella.
Una es "asignar una matriz de filas". Se parece a esto:
int (*rows)[NUM_COLS] = malloc(sizeof(*rows) * NUM_ROWS);
...
free(rows);
Entonces la sintaxis para acceder al elemento [i, j] es rows[i][j]
. En C89, NUM_COLS
debe conocerse en tiempo de compilación. Esta es una verdadera matriz 2D.
Una es "asignar una matriz de 1 d y pretender". Se parece a esto:
int *matrix = malloc(sizeof(int) * NUM_COLS * NUM_ROWS);
...
free(matrix);
Entonces, la sintaxis para acceder al elemento [i, j] es la matrix[NUM_COLS * i + j]
. Esto (por supuesto) no es una verdadera matriz 2D. En la práctica, tiene el mismo diseño que uno.
Si desea utilizar una matriz typedef''d, es aún más simple.
Digamos que tiene en su código typedef int LabeledAdjMatrix[SIZE][SIZE];
Puede usar:
LabeledAdjMatrix *pMatrix = malloc(sizeof(LabeledAdjMatrix));
Entonces puedes escribir:
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) (*parr)[i][j] = k++; /* or parr[0][i][j]... */
}
Porque pArr
es un puntero a su matriz y *
tiene una prioridad menor que []
;
Es por eso que una expresión común de modo es tipificar la fila:
typedef int LabeledAdjRow[SIZE];
Entonces puedes escribir:
LabeledAdjRow *pMatrix = malloc(sizeof(LabeledAdjRow) * SIZE);
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) parr[i][j] = k++;
}
Y puedes memcpy
todo eso directamente:
LabeledAdjRow *pOther = malloc(sizeof(LabeledAdjRow) * SIZE);
memcpy(pOther, pMatrix, sizeof(LabeledAdjRow) * SIZE);
Estáticamente hablando , esto es fácil de entender:
int mtx[3][2] = {{1, 2},
{2, 3},
{3, 4}};
Nada complicado aquí. 3 filas, 2 columnas; datos en la columna uno: 1, 2, 3
; datos en la columna dos: 2, 3, 4
. Podemos acceder a los elementos a través de la misma construcción:
for(i = 0; i<3; i++){
for(j = 0; j<2; j++)
printf("%d ", mtx[i][j]);
printf("/n");
}
//output
//1 2
//2 3
//3 4
Ahora veamos esto en términos de Punteros :
Los corchetes son una construcción muy buena para ayudar a simplificar las cosas, pero no ayuda cuando tenemos que trabajar en un entorno dinámico, por lo que tenemos que pensar en esto en términos de indicadores. Si queremos almacenar una "fila" de enteros, necesitamos una matriz:
int row[2] = {1,2};
¿Y sabes qué? Podemos acceder a esto como un puntero.
printf("%d, %d/n",*row,*(row+1)); //prints 1, 2
printf("%d, %d/n",row[0],row[1]); //prints 1, 2
Ahora bien, si no conocemos el número de valores en una fila, podemos hacer que esta matriz tenga una longitud dinámica si tenemos un puntero a int, y le damos algo de memoria:
int *row = malloc(X * sizeof(int)); //allow for X number of ints
*row = 1; //row[0] = 1
*(row+1) = 2; //row[1] = 2
…
*(row+(X-1)) = Y; // row[x-1] = Some value y
Entonces ahora tenemos una matriz dinámica de 1 dimensión; una sola fila Pero queremos muchas filas, no solo una, y no sabemos cuántas. Eso significa que necesitamos otra matriz dinámica de 1 dimensión, cada elemento de esa matriz será un puntero que apunta a una fila.
//we want enough memory to point to X number of rows
//each value stored there is a pointer to an integer
int ** matrix = malloc(X * sizeof(int *));
//conceptually:
(ptr to ptr to int) (pointer to int)
**matrix ------------> *row1 --------> [1][2]
*row2 --------> [2][3]
*row3 --------> [3][4]
Ahora todo lo que queda por hacer es escribir el código que realizará estas asignaciones dinámicas:
int i, j, value = 0;
//allocate memory for the pointers to rows
int ** matrix = malloc(Rows * sizeof(int*));
//each row needs a dynamic number of elements
for(i=0; i<Rows; i++){
// so we need memory for the number of items in each row…
// we could call this number of columns as well
*(matrix + i) = malloc(X * sizeof(int));
//While we’re in here, if we have the items we can populate the matrix
for(j=0; j<X; j++)
*(*(matrix+i)+j) = value; // if you deference (matrix + i) you get the row
// if you add the column and deference again, you
// get the actual item to store (not a pointer!)
}
Una de las cosas más importantes que debemos hacer ahora es asegurarnos de liberar la memoria cuando hayamos terminado. Cada nivel de malloc()
debería tener el mismo número de llamadas free()
, y las llamadas deberían estar en un orden FILO (inverso a las llamadas malloc):
for(i=0; i<Rows; i++)
free(*(matrix + i));
free(matrix);
//set to NULL to clean up, matrix points to allocated memory now so let’s not use it!
matrix = NULL;
En C desde C99, incluso las matrices multidimensionales dinámicas se pueden asignar fácilmente de una vez con malloc
y liberarse con free
:
double (*A)[n] = malloc(sizeof(double[n][n]));
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < n; ++j)
A[i][j] = someinvolvedfunction(i, j);
free(A);