matriz de asignación dinámica de matrices en C

6 minutos de lectura

avatar de usuario
hiperbóreo

Realmente no entiendo algunas cosas básicas en C, como la asignación dinámica de matrices. Sé que puedes hacer:

int **m;

para declarar una matriz bidimensional (que posteriormente se asignaría utilizando alguna función * alloc). También se puede acceder “fácilmente” haciendo *(*(m + line) + column). Pero, ¿cómo debo asignar un valor a un elemento de esa matriz? Usando gcc la siguiente declaración m[line][column] = 12; falla con una falla de segmentación.

Cualquier artículo/docs será apreciado. 🙂

  • Esto no es una “matriz de matrices”.

    – demasiado honesto para este sitio

    24 de junio de 2016 a las 14:27

avatar de usuario
jpalecek

los m[line][column] = 12 la sintaxis está bien (siempre y cuando line y column están dentro del rango).

Sin embargo, no escribió el código que usa para asignarlo, por lo que es difícil determinar si es incorrecto o correcto. Debe ser algo a lo largo de las líneas de

m = (int**)malloc(nlines * sizeof(int*));

for(i = 0; i < nlines; i++)
  m[i] = (int*)malloc(ncolumns * sizeof(int));

Algunas notas al margen:

  • De esta manera, puede asignar cada línea con una longitud diferente (por ejemplo, una matriz triangular)
  • Puede reasignar () o liberar () una línea individual más tarde mientras usa la matriz
  • Debe liberar () cada línea, cuando libera () toda la matriz

  • @jpalecek: la forma en que asigné memoria para eso fue: m = malloc (líneas * columnas * tamaño de (int *));

    – hiperbóreo

    18 de enero de 2009 a las 21:55

  • @hyperboreean: Eso asignaría una matriz unidimensional de punteros. Lo que desea es una matriz de punteros que cada uno apunte a otra matriz. Primero debe asignar la matriz de punteros, luego asignar memoria para cada matriz a la que se apunta.

    – Paige Ruten

    18 de enero de 2009 a las 22:03

  • Wow, dos ediciones patéticamente inútiles. @chakrit: lanzar malloc generalmente se considera una mala práctica en C, ya que no es necesario y puede enmascarar errores. +1 por la respuesta original.

    – Roberto Gamble

    18 de enero de 2009 a las 22:24

  • @Robert, desafortunadamente depende de la implementación del compilador. Algunos compiladores más antiguos tienen mallocs no estándar que requieren una conversión. VisualStudio fue así hasta hace poco (no recuerdo la versión exacta)

    – JaredPar

    18 de enero de 2009 a las 22:48

  • Convertir malloc es una buena práctica en C++ porque es necesario si la variable en el LHS no es un puntero vacío. Es una mala práctica en C porque las advertencias que elimina casi siempre son errores que deben abordarse adecuadamente y eliminar la advertencia solo trata el síntoma.

    – Roberto Gamble

    19 de enero de 2009 a las 0:47

Tu sintaxis m[line][colummn] es correcto. Pero para usar una matriz 2D en C, debe asignarle memoria. Por ejemplo, este código asignará memoria para una tabla de línea y columna dadas.

int** AllocateArray(int line, int column) {
  int** pArray = (int**)malloc(line*sizeof(int*));
  for ( int i = 0; i < line; i++ ) {
    pArray[i] = (int*)malloc(column*sizeof(int));
  }
  return pArray;
}

Tenga en cuenta que omití las comprobaciones de error de malloc por brevedad. Una solución real debería incluirlos.

  • Hay varias formas de crear dinámicamente “matrices multidimensionales” en C con diferencias sutiles pero importantes, consulte c-faq.com/aryptr/dynmuldimary.html para detalles. Además, como advertencia para los demás sobre los peligros de lanzar malloc: c-faq.com/malloc/mallocnocast.html.

    – Roberto Gamble

    19 de enero de 2009 a las 6:01

No es una matriz 2d, es una matriz de matrices, por lo que necesita múltiples asignaciones.

  • Probablemente este fue mi problema de que estaba recibiendo una falla de segmentación.

    – hiperbóreo

    18 de enero de 2009 a las 22:02

avatar de usuario
Cristóbal

Aquí hay una versión modificada de la solución de quinmars que solo asigna un bloque de memoria y se puede usar con valores genéricos por cortesía de void *:

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

void ** array2d(size_t rows, size_t cols, size_t value_size)
{
    size_t index_size = sizeof(void *) * rows;
    size_t store_size = value_size * rows * cols;

    char * a = malloc(index_size + store_size);
    if(!a) return NULL;

    memset(a + index_size, 0, store_size);
    for(size_t i = 0; i < rows; ++i)
        ((void **)a)[i] = a + index_size + i * cols * value_size;

    return (void **)a;
}

int printf(const char *, ...);

int main()
{
    int ** a = (int **)array2d(5, 5, sizeof(int));
    assert(a);
    a[4][3] = 42;
    printf("%i\n", a[4][3]);
    free(a);
    return 0;
}

No estoy seguro de si es realmente seguro lanzar void ** para int ** (Creo que el estándar permite que se realicen conversiones al convertir a/desde void * ?), pero funciona en gcc. Para estar seguro, debe reemplazar cada ocurrencia de void * con int *


Las siguientes macros implementan una versión con seguridad de tipos del algoritmo anterior:

#define alloc_array2d(TYPE, ROWS, COLS) \
    calloc(sizeof(TYPE *) * ROWS + sizeof(TYPE) * ROWS * COLS, 1)

#define init_array2d(ARRAY, TYPE, ROWS, COLS) \
    do { for(int i = 0; i < ROWS; ++i) \
        ARRAY[i] = (TYPE *)(((char *)ARRAY) + sizeof(TYPE *) * ROWS + \
        i * COLS * sizeof(TYPE)); } while(0)

Úsalos así:

int ** a = alloc_array2d(int, 5, 5);
init_array2d(a, int, 5, 5);
a[4][3] = 42;

Aunque estoy de acuerdo con las otras respuestas, en la mayoría de los casos es mejor asignar toda la matriz a la vez, porque malloc es bastante lento.


int **
array_new(size_t rows, size_t cols)
{
    int **array2d, **end, **cur;
    int *array;

    cur = array2d = malloc(rows * sizeof(int *));
    if (!array2d)
        return NULL;

    array = malloc(rows * cols * sizeof(int));
    if (!array)
    {
        free(array2d);
        return NULL;
    }

    end = array2d + rows;
    while (cur != end)
    {
        *cur = array;
        array += cols;
        cur++;
    }

    return array2d;
}

Para liberar la matriz simplemente haga:
free(*array); free(array);

Nota: esta solución solo funciona si no desea cambiar el orden de las filas, ya que podría perder la dirección del primer elemento, que necesita para liberar la matriz más adelante.

  • Esa es una solución ingeniosa 🙂 Aunque si quisiera asignarlo de una vez, probablemente recurriría a la “matriz[i*cols+j]” direccionar y deshacerse de la matriz “array2d”.

    – jpalecek

    18 de enero de 2009 a las 23:39

avatar de usuario
EvilTeach

Humm. ¿Qué tal el humo antiguo y los espejos como opción?

#define ROWS  5
#define COLS 13
#define X(R, C) *(p + ((R) * ROWS) + (C))

int main(void)
{
    int *p = (int *) malloc (ROWS * COLS * sizeof(int));
    if (p != NULL)
    {
        size_t r;
        size_t c;
        for (r = 0; r < ROWS; r++)
        {
            for (c = 0; c < COLS; c++)
            {
                 X(r,c) = r * c;  /* put some silly value in that position */ 
            }
        }

        /* Then show the contents of the array */ 
        for (r = 0; r < ROWS; r++)
        {
            printf("%d ", r);   /* Show the row number */ 

            for (c = 0; c < COLS; c++)
            {
                 printf("%d", X(r,c));
            }

            printf("\n");
        }

        free(p);
    }
    else
    {
        /* issue some silly error message */ 
    }

    return 0;
}

  • Esa es una solución ingeniosa 🙂 Aunque si quisiera asignarlo de una vez, probablemente recurriría a la “matriz[i*cols+j]” direccionar y deshacerse de la matriz “array2d”.

    – jpalecek

    18 de enero de 2009 a las 23:39

avatar de usuario
Keltia

Utilizando malloc(3) para asignar la primera matriz y poner allí punteros creados por malloc(3) debería trabajar con array[r][c] porque debe ser equivalente a *(*(array + r) + c)está en el estándar C.

¿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