definir una matriz 2D con malloc y modificarla

11 minutos de lectura

¿Cómo defino una matriz 2D usando malloc? (digamos 10X20).

segundo, ¿puedo aumentar la cantidad de filas o columnas sin crear una nueva matriz aumentada y copiar todos los datos en ella?

por ejemplo, ¿cómo asigno memoria para que la matriz sea 10×30 o 15×20?

¡gracias!

avatar de usuario
Johannes Schaub – litb

10×30:

int(*array)[30] = malloc((sizeof *array) * 10);

15×20:

int(*array)[20] = malloc((sizeof *array) * 15);

Cambiar el tamaño a 20×25:

int(*array2)[25] = realloc(array, (sizeof *array2) * 20);

La dimensión exterior (10, 15, 20) se puede determinar en tiempo de ejecución, porque no es necesaria como parte de los cálculos de índice por parte del compilador. La dimensión interna (30, 20, 25) debe conocerse en tiempo de compilación. Espero eso ayude.

Tenga en cuenta que, a diferencia de las soluciones de matriz de punteros, esta puede manejarse como un solo bloque de memoria, ya que asigna todo en una sola porción de memoria como una matriz declarada real:

memcpy(somewhere, array2, sizeof(int) * 20 * 25); // (sizeof *array2) * 20

Sin embargo, en última instancia, depende de su caso de uso.


Dado que algunas personas tienen dificultades para comprender las acciones realizadas por una operación de índice para arrayveamos lo que nos da Clang para una expresión de índice en el siguiente código

int main() {
  int(*array)[10] = malloc((sizeof *array) * 5);
  array[4][9] = 0;

  int(*array1)[10][5] = malloc((sizeof *array1) * 20);
  array1[19][9][4] = 0;
}

Es un buen compilador, que puede imprimir su AST de una manera fácil de leer.

  // array[4][9] = 0;
  (BinaryOperator 0xba62cc0 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xba62c80 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xba62c60 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba62c20 <col:3, col:10> 'int [10]'
          (DeclRefExpr 0xba62bdc <col:3> 'int (*)[10]' Var="array" 0xba62a00)
          (IntegerLiteral 0xba62c00 <col:9> 'int' 4)))
      (IntegerLiteral 0xba62c40 <col:12> 'int' 9))
    (IntegerLiteral 0xba62ca0 <col:17> 'int' 0))

  // array1[19][9][4] = 0;
  (BinaryOperator 0xba630b8 <line:8:3, col:22> 'int' '='
    (ArraySubscriptExpr 0xba63078 <col:3, col:18> 'int'
      (ImplicitCastExpr 0xba63058 <col:3, col:15> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xba63018 <col:3, col:15> 'int [5]'
          (ImplicitCastExpr 0xba62ff8 <col:3, col:12> 'int (*)[5]' <ArrayToPointerDecay>
            (ArraySubscriptExpr 0xba62fa0 <col:3, col:12> 'int [10][5]'
              (DeclRefExpr 0xba62f5c <col:3> 'int (*)[10][5]' Var="array1" 0xba62db0)
              (IntegerLiteral 0xba62f80 <col:10> 'int' 19)))
          (IntegerLiteral 0xba62fc0 <col:14> 'int' 9)))
      (IntegerLiteral 0xba63038 <col:17> 'int' 4))
    (IntegerLiteral 0xba63098 <col:22> 'int' 0)))

Tenga en cuenta que cada expresión de subíndice de matriz toma un puntero, agrega el valor del índice y produce el elemento direccionado. Si ese subelemento es una matriz, se descompone en un puntero a su primer elemento. Esto realmente no es diferente de los pasos realizados para una matriz declarada

int main() {
  int array[5][10] = { };
  array[4][9] = 1;
}

Produce un AST muy similar, con solo la expresión más interna primero decayendo en un puntero a su primer elemento

  // array[4][9] = 1;
  (BinaryOperator 0xbf9f7e8 <line:5:3, col:17> 'int' '='
    (ArraySubscriptExpr 0xbf9f7a8 <col:3, col:13> 'int'
      (ImplicitCastExpr 0xbf9f788 <col:3, col:10> 'int *' <ArrayToPointerDecay>
        (ArraySubscriptExpr 0xbf9f748 <col:3, col:10> 'int [10]'
          (ImplicitCastExpr 0xbf9f728 <col:3> 'int (*)[10]' <ArrayToPointerDecay>
            (DeclRefExpr 0xbf9f6cc <col:3> 'int [5][10]' Var="array" 0xbfa81f0))
          (IntegerLiteral 0xbf9f6f0 <col:9> 'int' 4)))
      (IntegerLiteral 0xbf9f768 <col:12> 'int' 9))
    (IntegerLiteral 0xbf9f7c8 <col:17> 'int' 1)))

  • Espera, estoy un poco confundido. no es int(*array)[30] un puntero a la matriz 30 de int? Y está asignando este puntero a una ubicación de sizeof(int) * 10 bytes grande. Esto significa que esta ubicación solo puede contener 10 enteros. ¿Dónde ocurre la asignación para el resto de los bytes? Porque necesita 30×10 ints (300 ints). No sabía que poner el [] en la definición replicaría la ubicación asignada a las matrices…. ¿cómo funciona esto?

    –Luca Matteis

    27 de agosto de 2010 a las 14:44

  • @Luca, sí, es un puntero a una matriz de 30 int. Pero sizeof *array rendimientos sizeof(int[30]) cual es sizeof(int) * 30. No sé a qué te refieres con “replicaría la ubicación asignada a las matrices”. Déjame intentar explicarlo: el primer código simplemente asigna un búfer que tiene un tamaño sizeof(int[10][30]) y hace un puntero a int[30] punto a esto. A continuación, puede indexar en el rango de array[0..9][0..29].

    – Johannes Schaub – litb

    27 de agosto de 2010 a las 14:48


  • Ohhhhhh, inteligente :). Gracias por enseñarme algo nuevo. +1.

    –Luca Matteis

    27 de agosto de 2010 a las 14:49

  • @Johannes: Pero espera… ¿aún puedes acceder a una ubicación de memoria usando la desreferenciación de doble puntero? array[5][10]? No estoy seguro de cómo funcionaría eso, ya que es solo un bloque de memoria como dijiste. que seria array[5] ¿ser?

    –Luca Matteis

    27 de agosto de 2010 a las 14:52


  • array[5] sería int[30]. Tenga en cuenta que es *(array + 5)que agrega sizeof(int[30]) * 5 bytes a la dirección almacenada en la matriz. Con el int[30] y el int* decae esto produce, la segunda operación de índice puede tener lugar y finalmente producir el int lvalor. Así es como int array[10][30]; funciona también Una vez que te refieres array en una operación de índice, se descompone y se convierte en int(*)[30].

    – Johannes Schaub – litb

    27 ago 2010 a las 15:00


Tiempo malloc() no admite matrices multidimensionales directamente, existen soluciones alternativas, como:

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

// Element (5,6)
int x = 5;
int y = 6;
int element = array [ x * cols + y ];

Si bien esto no es directamente una matriz 2D, funciona y, en mi opinión, es el más simple. Pero si quieres usar el [][] sintaxis en su lugar, tendría que hacer punteros a punteros, por ejemplo:

int rows = 10;
int cols = 30;
// Rows
int **array = malloc(rows * sizeof(int*));
// Cols
int i;
for(i = 0; i < rows; i++)
  array[i] = malloc(cols * sizeof(int));

// Element (5,6)
int x = 5;
int y = 6;
int element = array[x][y];

  • +1 – Encuentro de esta manera mucho más simple y menos propensa a errores que la solución de matriz de matrices.

    – pastel

    27 de agosto de 2010 a las 14:09

  • Creo que usaré tu primer ejemplo, es mucho más simple. Gracias a ti y a los demás por tus respuestas.

    – Asher Sabán

    27 de agosto de 2010 a las 14:22

  • mm, solo una pregunta más, con el segundo ejemplo, ¿puedo crear una matriz no cuadrada? como en java: arr[][] = {1,2,3,4},{1,2} ?

    – Asher Sabán

    27 de agosto de 2010 a las 14:24

  • @rob Sí, por supuesto, puede crear matrices irregulares en C como en Java. En línea de código: matriz[i] = malloc(cols * sizeof(int)) simplemente sustituya cualquier número en lugar de la variable COLS. Eso es todo.

    – Agnius Vasiliauskas

    27 de agosto de 2010 a las 17:13

avatar de usuario
luca mateis

// first allocate the 20 rows, that contain pointers to int 
// (if the matrix contains int type values)
int **a = malloc(20 * sizeof(int *));

// now loop for each row and allocate data for the actual values
int i;
for(i = 0; i < 20; i++) {
    a[i] = malloc(10 * sizeof(int));
}

Para aumentar el tamaño de la matriz se puede utilizar realloc aunque probablemente sería más fácil regenerar la matriz de diferente tamaño y copiar los valores.

  • Esta imagen parece una explicación de C++. Tenga en cuenta que si esto está explicando el C++ new operador, está mal. new crea un verdadero matriz de matrices, que se parecerá al primer diagrama (solo asigna 3 * 4 enteros, como matrices declaradas), pero no como el segundo diagrama (asigna 3 * 4 enteros y 4 punteros). Por supuesto, la descripción es correcta si se aplica a su código de bucle en lugar de new int[3][4].

    – Johannes Schaub – litb

    27 de agosto de 2010 a las 14:15


  • sí, en realidad quería hacer referencia solo al segundo diagrama, déjame deshacerme de él.

    –Luca Matteis

    27 de agosto de 2010 a las 14:32

segundo, ¿puedo aumentar la cantidad de filas o columnas sin crear una nueva matriz aumentada y copiar todos los datos en ella?

No, no puede cambiar el tamaño de una matriz. Lo que puede hacer es usar punteros y realloc para hacerlo.

Una matriz 2D es una matriz 1D de matrices 1D. Como una matriz es simplemente un puntero, una matriz de matrices es una matriz de punteros. Entonces, usas malloc para asignar una matriz de punteros (cada uno representando una columna), luego utilícelo nuevamente para asignar las matrices individuales (cada uno representando una fila).

Para expandir/reducir la matriz, usa realloc(referencia). Aquí hay un código de ejemplo:

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

int** alloc_matrix(int w, int h) {
    int** matrix;

    matrix = (int**) malloc(sizeof(int*) * h);
    for(int i = 0; i < h; i++) {
        matrix[i] = (int*) malloc(sizeof(int) * w);
    }

    return matrix;
}

void realloc_matrix(int*** matrix, int new_w, int new_h) {
    *matrix = (int**) realloc((void *)*matrix, sizeof(int*) * new_h);

    for(int i = 0; i < new_h; i++) {
        (*matrix)[i] = (int*) realloc((void *)(*matrix)[i], sizeof(int) * new_w);
    }
}

int main(int argc, char* argv[]) {
    // Allocating matrix
    int** m = alloc_matrix(10, 15);

    // Filling with some data
    for(int y = 0; y < 15; y++) {
        for(int x = 0; x < 10; x++) {
            m[y][x] = y * x; // Notice the index is [y][x] - NOT [x][y]!
        }
    }

    // Printing
    for(int y = 0; y < 15; y++) {
        for(int x = 0; x < 10; x++) {
            printf("%d\t", m[y][x]); // Notice the index is [y][x] - NOT [x][y]!
        }
        printf("\n");
    }

    // Reallocating
    realloc_matrix(&m, 20, 10);

    // Filling with some data
    for(int y = 0; y < 10; y++) {
        for(int x = 0; x < 20; x++) {
            m[y][x] = y * x; // Notice the index is [y][x] - NOT [x][y]!
        }
    }

    // Printing
    for(int y = 0; y < 10; y++) {
        for(int x = 0; x < 20; x++) {
            printf("%d\t", m[y][x]); // Notice the index is [y][x] - NOT [x][y]!
        }
        printf("\n");
    }
}

Lo siento si tengo algo mal. Mi C-fu está un poco oxidado 🙂

avatar de usuario
Andrei Ciobanu

En lugar de usar int[row][col] deberías “envolver tu matriz” en una matriz unidimensional int[row*col].

Aquí hay un código de muestra:

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

#define DET_MALLOC_FAIL -1
#define DET_READ_FAIL -2
#define DET_WRITE_FAIL -3

typedef struct det_matrix_s {
    double * vect;
    size_t order;
} det_matrix;

void *det_sf_malloc(size_t dsize);
det_matrix * det_matrix_new(size_t order);
#define DET_MAT_ELEM(matr, i, j) \
    ((matr)->vect[((matr)->order * (i)) + (j)])
int det_matrix_read(det_matrix * matr, FILE * src);
int det_matrix_write(det_matrix * matr, FILE * dest);
void det_matrix_delete(det_matrix * matr);

/**
* Malloc wrapper
*/
void * det_sf_malloc(size_t dsize)
{
    void *data = malloc(dsize);
    if(NULL == data){
        exit(DET_MALLOC_FAIL);
    }
    return (data);
}

/**
* Allocates memory for a new matrix
*/
det_matrix * det_matrix_new(size_t order)
{
    det_matrix * res = det_sf_malloc(1 * sizeof(*res));
    double * vect = det_sf_malloc(order * order * sizeof(*vect));
    res->order = order;
    res->vect = vect;
    return (res);
}

/**
* Reads matrix 
*/
int det_matrix_read(det_matrix * matr, FILE * src)
{
    size_t i, j;
    if(NULL == matr || NULL == src){
        return (DET_READ_FAIL);
    }
    for(i = 0; i < matr->order; ++i){
        for(j = 0; j < matr->order; ++j){
            if(stdin == src){
                fprintf(stdout, "mat[%d][%d] = ", i, j);
            }
            fscanf(src, "%lf", &DET_MAT_ELEM(matr, i, j));
        }
    }
    return (EXIT_SUCCESS);
}

/**
* Writes matrix
**/
int det_matrix_write(det_matrix * matr, FILE * dest)
{
    size_t i, j;
    if(NULL == matr || NULL == dest){
        return (DET_WRITE_FAIL);
    }
    for(i = 0; i < matr->order; ++i){
        for(j = 0; j < matr->order; ++j){
            fprintf(dest, "%5.2lf ", DET_MAT_ELEM(matr, i, j));
        }
        fprintf(dest, "\n");    
    }
    return (EXIT_SUCCESS);
}

/**
* Free memory for matrix
*/
void det_matrix_delete(det_matrix * matr)
{
    free(matr->vect);
    free(matr);
}

/**
* Main
*/ 
int main(int argc, char * argv[])
{
    det_matrix * mat = det_matrix_new(3);
    det_matrix_read(mat, stdin);
    det_matrix_write(mat, stdout);
    det_matrix_delete(mat);
    return (EXIT_SUCCESS);
}

Si la reasignación es un problema, pueden aparecer problemas. Y deberías usar la versión bidimensional.

Aunque esta es una vieja pregunta, aquí está mi solución diferente.

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

int main(int arvc, char* argv[])
{
    int (*a)[5][8];
    int i, j;

    a = (int (*)[5][8])calloc(5*8, sizeof(int));        
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 8; j++)
            (*a)[i][j] = i *10 + j;
    }

    for (i = 0; i < 5; i++) {
        for (j = 0; j < 8; j++)
            printf("%d ", (*a)[i][j]);
        printf("\n");
    }

    return 0;
}

Compilar y ejecutar

[user@buzz ~]$ gcc main.c -o main
[user@buzz ~]$
[user@buzz ~]$ ./main
0 1 2 3 4 5 6 7
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
30 31 32 33 34 35 36 37
40 41 42 43 44 45 46 47
[user@buzz ~]$

¿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