¿Cómo trabajo con arreglos multidimensionales dinámicos en C?

13 minutos de lectura

avatar de usuario
RPF

¿Alguien sabe cómo puedo usar matrices multidimensionales asignadas dinámicamente usando C? ¿Es eso posible?

avatar de usuario
schnaader

Con asignación dinámica, usando malloc:

int** x;

x = malloc(dimension1_max * sizeof(*x));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = malloc(dimension2_max * sizeof(x[0]));
}

//Writing values
x[0..(dimension1_max-1)][0..(dimension2_max-1)] = Value; 
[...]

for (int i = 0; i < dimension1_max; i++) {
  free(x[i]);
}
free(x);

Esto asigna una matriz 2D de tamaño dimension1_max * dimension2_max. Entonces, por ejemplo, si desea una matriz de 640 * 480 (por ejemplo, píxeles de una imagen), use dimension1_max = 640, dimension2_max = 480. Luego puede acceder a la matriz usando x[d1][d2] donde d1 = 0..639, d2 = 0..479.

Pero una búsqueda en SO o Google también revela otras posibilidades, por ejemplo en esta pregunta SO

Tenga en cuenta que su matriz no asignará una región contigua de memoria (640 * 480 bytes) en ese caso, lo que podría generar problemas con las funciones que asumen esto. Entonces, para que la matriz satisfaga la condición, reemplace el bloque malloc de arriba con esto:

int** x;
int* temp;

x = malloc(dimension1_max * sizeof(*x));
temp = malloc(dimension1_max * dimension2_max * sizeof(x[0]));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = temp + (i * dimension2_max);
}

[...]

free(temp);
free(x);

  • Esto no se compilará, debe declarar x como “int **”, no “int[][]”.

    – Adam Rosenfield

    27 de mayo de 2009 a las 20:28

  • ¿Qué hace esto exactamente? ¿Qué es dimension1_max y dimension2_max? ¿Qué significa el primer “para”?

    – rpf

    27 mayo 2009 a las 20:35

  • Gracias, Adam, sabía que algo andaba mal aunque ya corregí el primer sizeof a int* en lugar de int.

    – schnaader

    27 de mayo de 2009 a las 20:39

  • No es una matriz multidimensional: es una matriz de punteros a int o una matriz de matrices. Para asignar memoria para una matriz 2D real, debe usar malloc (dim1 * dim2 * sizeof (int)). Si alguna función espera un puntero a una matriz 2D, como foo(int * bar[5][6]) y pasas tu x, van a pasar cosas raras. Ver en.wikipedia.org/wiki/C_syntax#Multidimensional_arrays

    – qrdl

    28 de mayo de 2009 a las 6:45

  • Esto hace no asigne una matriz 2D, pero una matriz de puntero más matrices a las que apunta. El direccionamiento es muy diferente, tiene sobrecarga de memoria y probablemente sea menos eficiente.

    – demasiado honesto para este sitio

    15 de febrero de 2016 a las 14:20

avatar de usuario
Jens Gustedt

Desde C99, C tiene matrices 2D con límites dinámicos. Si desea evitar que tales bestias se asignen en la pila (que debería), puede asignarlas fácilmente de una vez de la siguiente manera

double (*A)[n] = malloc(sizeof(double[n][n]));

y eso es. Luego puede usarlo fácilmente como lo usa para matrices 2D con algo como A[i][j]. Y no olvides que uno al final

free(A);

Randy Meyers escribió una serie de artículos explicando matrices de longitud variable (VLA).

  • Partes 2, 3y 4 de la serie de Meyers también están disponibles del Dr. Dobbs.

    – Phil Miller

    1 de noviembre de 2013 a las 21:19

  • Aunque esta es una solución agradable y elegante, la belleza está más en la declaración de A que en la asignación real; en particular, no se asigna ninguna matriz 2-D en ningún lugar, y mucho menos una de tamaño dinámico. El malloc solo asigna un tramo lineal de memoria sin tipo adecuadamente alineado para cualquier cosa; la dirección A se inicializa podría provenir de cualquier fuente (bueno, debido a problemas de alias, supongo que podría provenir de una matriz unidimensional de char o double, o una matriz bidimensional “verdadera”). el tiempo de ejecución sizeof simplemente calcula un número (siempre el correcto, por supuesto) ;-).

    – Peter – Reincorporar a Mónica

    15 de febrero de 2016 a las 15:51

  • ¡Gran respuesta! Adición: en su ejemplo, ambas dimensiones tienen el mismo tamaño n. Con diferentes tamaños, se vería así: double (*a)[y] = malloc(sizeof(double[x][y]));. Con tres dimensiones: double (*a)[y][z] = malloc(sizeof(double[x][y][z]));. Y así.

    – jcsahnwaldt Reincorporar a Monica

    8 oct 2017 a las 10:20

  • La única buena respuesta a esta pregunta. Sin embargo, siempre preferiría usar la variable de puntero en el sizeof() Me gusta esto: double (*A)[n] = malloc(n*sizeof(*A)); La ventaja es que no puede equivocarse en los detalles del tipo en el sizeof()y es siempre la misma forma de arrayLength*sizeof(*newPointerVar) como el malloc() argumento. Creo que este formulario tiene la oportunidad más baja de que se introduzcan errores.

    – cmaster – reincorporar a monica

    17 de enero de 2020 a las 12:18

avatar de usuario
dmckee — gatito ex-moderador

Lo esencial

Los arreglos en c son declarados y accedidos usando el [] operador. Así que eso

int ary1[5];

declara una matriz de 5 enteros. Los elementos se numeran desde cero por lo que ary1[0] es el primer elemento y ary1[4] es el último elemento. Nota 1: no existe una inicialización predeterminada, por lo que la memoria ocupada por la matriz puede contener inicialmente cualquier cosa. Nota 2: ary1[5] accede a la memoria en un estado indefinido (que puede que ni siquiera sea accesible para usted), ¡así que no lo haga!

Los arreglos multidimensionales se implementan como un arreglo de arreglos (de arreglos (de…)). Asi que

float ary2[3][5];

declara una matriz de 3 matrices unidimensionales de 5 números de coma flotante cada una. Ahora ary2[0][0] es el primer elemento de la primera matriz, ary2[0][4] es el último elemento de la primera matriz, y ary2[2][4] es el último elemento de la última matriz. El estándar ’89 requiere que estos datos sean contiguos (sec. A8.6.2 en la página 216 de mi K&R 2nd. ed.) pero parece ser agnóstico en el relleno.

Tratando de ser dinámico en más de una dimensión

Si no conoce el tamaño de la matriz en el momento de la compilación, querrá asignar la matriz dinámicamente. Es tentador probar

double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */

que debería funcionar si el compilador no rellena la asignación (pegue espacio adicional entre las matrices unidimensionales). Podría ser más seguro ir con:

double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */

pero de cualquier manera, el truco viene en el momento de la desreferenciación. no puedes escribir buf[i][j] porque buf tiene el tipo incorrecto. Tampoco puedes usar

double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */

porque el compilador espera hdl4 ser la dirección de una dirección de un doble. Tampoco puedes usar double incomplete_ary4[][]; porque esto es un error;

¿Entonces que puedes hacer?

  • Haz tú mismo la aritmética de filas y columnas
  • Asignar y hacer el trabajo en una función
  • Use una matriz de punteros (el mecanismo del que habla qrdl)

Haz los cálculos tú mismo

Simplemente calcule el desplazamiento de memoria para cada elemento de esta manera:

  for (i=0; i<3; ++i){
     for(j=0; j<3; ++j){
        buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about 
                                             padding in this case */
     }
  }

Asignar y hacer el trabajo en una función

Defina una función que tome el tamaño necesario como argumento y proceda normalmente

void dary(int x, int y){
  double ary4[x][y];
  ary4[2][3] = 5;
}

Por supuesto, en este caso ary4 es una variable local y no puede devolverla: todo el trabajo con la matriz debe realizarse en la función a la que llama en funciones que eso llamadas

Una matriz de punteros

Considera esto:

double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
   hdl5[i] = malloc(5*sizeof(double))
   /* Error checking */
}

Ahora hdl5 apunta a una matriz de punteros, cada uno de los cuales apunta a una matriz de dobles. Lo bueno es que puede usar la notación de matriz bidimensional para acceder a esta estructura —hdl5[0][2] obtiene el elemento del medio de la primera fila — pero este es, sin embargo, un tipo de objeto diferente a una matriz bidimensional declarada por double ary[3][5];.

Esta estructura es más flexible que una matriz bidimensional (porque las filas no necesitan tener la misma longitud), pero el acceso generalmente será más lento y requiere más memoria (necesita un lugar para colocar los punteros intermedios).

Tenga en cuenta que, dado que no he configurado ningún protector, tendrá que realizar un seguimiento del tamaño de todas las matrices usted mismo.

Aritmética

c no proporciona soporte para matemáticas vectoriales, matriciales o tensoriales, tendrá que implementarlo usted mismo o traer una biblioteca.

La multiplicación por un escalador y la suma y resta de matrices del mismo rango son fáciles: simplemente recorra los elementos y realice la operación sobre la marcha. Los productos internos son igualmente sencillos.

Los productos exteriores significan más bucles.

  • Solo un punto: la matriz de matrices es una matriz de punteros, no una matriz multidimensional real. en.wikipedia.org/wiki/C_syntax#Multidimensional_arrays

    – qrdl

    28 de mayo de 2009 a las 6:50

  • @qrdl: mientras leo el estándar int **a1; e int *a2[i]; int a3[n][m]; son bichos diferentes con semántica diferente. Al último se le asigna un bloque de memoria contigua (posiblemente rellenada) sin un conjunto separado de punteros intermedios… Pruebe con printf(“%d\n”,sizeof(int[3][5])/tamaño(int));

    – dmckee — gatito ex-moderador

    28 de mayo de 2009 a las 14:53

  • ¿Funciona “asignar en una función”? ¿Cómo sabría el compilador cuánta memoria asignar en la pila para el arreglo ary4 si se desconocen x e y?

    – madera

    08/01/2015 a las 18:59

  • @MIkhail Er… x y y se conocen en tiempo de ejecución, y el compilador no está restringido a dimensionar el marco de la pila de una sola vez. Intentalo. Por supuesto, no puede devolver esa matriz, debe hacer todo el trabajo en la función, por lo que mi texto es un poco engañoso. Editaré para arreglar eso.

    – dmckee — gatito ex-moderador

    9 de enero de 2015 a las 0:59


  • @MIkhail Por lo que vale, creo que el compilador hace necesita saber el tamaño de las matrices de alcance global en tiempo de compilación.

    – dmckee — gatito ex-moderador

    9 de enero de 2015 a las 2:01

Si conoce la cantidad de columnas en tiempo de compilación, es bastante simple:

#define COLS ...
...
size_t rows;
// get number of rows
T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T

Puedes tratar ap como cualquier matriz 2D:

ap[i][j] = x;

Cuando hayas terminado, desasignarlo como

free(ap);

Si no conoce la cantidad de columnas en el momento de la compilación, pero está trabajando con un compilador C99 o un compilador C2011 que admite matrices de longitud variable, sigue siendo bastante simple:

size_t rows;
size_t cols;
// get rows and cols
T (*ap)[cols] = malloc(sizeof *ap * rows);
...
ap[i][j] = x;
...
free(ap);

Si no conoce la cantidad de columnas en el momento de la compilación y está trabajando con una versión de C que no admite matrices de longitud variable, deberá hacer algo diferente. Si necesita que todos los elementos se asignen en un fragmento contiguo (como una matriz regular), puede asignar la memoria como una matriz 1D y calcular un desplazamiento 1D:

size_t rows, cols;
// get rows and columns
T *ap = malloc(sizeof *ap * rows * cols);
...
ap[i * rows + j] = x;
...
free(ap);

Si no necesita que la memoria sea contigua, puede seguir un método de asignación de dos pasos:

size_t rows, cols;
// get rows and cols
T **ap = malloc(sizeof *ap * rows);
if (ap)
{
  size_t i = 0;
  for (i = 0; i < cols; i++)
  {
    ap[i] = malloc(sizeof *ap[i] * cols);
  }
}

ap[i][j] = x;

Dado que la asignación fue un proceso de dos pasos, la desasignación también debe ser un proceso de dos pasos:

for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);

Aquí está el código de trabajo que define una subrutina make_3d_array para asignar una matriz 3D multidimensional con N1, N2 y N3 elementos en cada dimensión, y luego lo rellena con números aleatorios. Puedes usar la notación A[i][j][k] para acceder a sus elementos.

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


// Method to allocate a 2D array of floats
float*** make_3d_array(int nx, int ny, int nz) {
    float*** arr;
    int i,j;

    arr = (float ***) malloc(nx*sizeof(float**));

    for (i = 0; i < nx; i++) {
        arr[i] = (float **) malloc(ny*sizeof(float*));

        for(j = 0; j < ny; j++) {
            arr[i][j] = (float *) malloc(nz * sizeof(float));
        }
    }

    return arr;
} 



int main(int argc, char *argv[])
{
    int i, j, k;
    size_t N1=10,N2=20,N3=5;

    // allocates 3D array
    float ***ran = make_3d_array(N1, N2, N3);

    // initialize pseudo-random number generator
    srand(time(NULL)); 

    // populates the array with random numbers
    for (i = 0; i < N1; i++){
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                ran[i][j][k] = ((float)rand()/(float)(RAND_MAX));
            }
        }
   }

    // prints values
    for (i=0; i<N1; i++) {
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                printf("A[%d][%d][%d] = %f \n", i,j,k,ran[i][j][k]);
            }
        }
    }

    free(ran);
}

  • Errores: en lugar de nx y ny tamaños en el bucle de make_3d_array(), tiene que ser ny y nz. Además, los recuerdos de corrió[i][j] y corrió[i] aquí no se liberan lo cual es muy malo. @rodrigo

    usuario3857354

    27 de noviembre de 2017 a las 1:34


avatar de usuario
Arkaprabha Banerjee

// use new en lugar de malloc ya que el uso de malloc conduce a pérdidas de memoria `ingrese el código aquí

    int **adj_list = new int*[rowsize];       
    for(int i = 0; i < rowsize; ++i)    
    {

        adj_list[i] = new int[colsize];

    }

  • Errores: en lugar de nx y ny tamaños en el bucle de make_3d_array(), tiene que ser ny y nz. Además, los recuerdos de corrió[i][j] y corrió[i] aquí no se liberan lo cual es muy malo. @rodrigo

    usuario3857354

    27 de noviembre de 2017 a las 1:34


Malloc lo hará.

 int rows = 20;
 int cols = 20;
 int *array;

  array = malloc(rows * cols * sizeof(int));

Consulte el siguiente artículo para obtener ayuda: –

http://courses.cs.vt.edu/~cs2704/spring00/mcquain/Notes/4up/Managing2DArrays.pdf

  • La pregunta pide C. Eso fue C++.

    – slugonamisión

    9 oct 2012 a las 16:20

  • @rahul Sugeriría que si realiza la edición que hizo anteriormente, deje el artículo que había vinculado. A pesar de que la sintaxis es específica de C ++, todos los conceptos presentados allí se mantendrían para el método que propone en su respuesta editada

    – Estoy tan confundida

    9 oct 2012 a las 16:26

  • @ AK4749: – Lo eliminé porque tenía miedo de perder mis puntos. ¡Había perdido muchos de ellos antes también porque las personas aquí son muy específicas para sus preguntas y no quieren explorar! De todos modos muchas gracias por impulsarme

    – Raúl Tripathi

    9 oct 2012 a las 16:29

  • @RahulTripathi sí, también he notado ese “analismo” sobre SO, pero en su defensa, no es un foro sino un lugar para obtener respuestas a preguntas específicas. Una vibra diferente a la que acostumbrarse, supongo :/

    – Estoy tan confundida

    9 oct 2012 a las 16:37

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad