malloc en C, pero use sintaxis de matriz multidimensional

7 minutos de lectura

avatar de usuario
claudio

¿Hay alguna forma de hacer malloc una matriz grande, pero referirse a ella con sintaxis 2D? quiero algo como:

int *memory = (int *)malloc(sizeof(int)*400*200);
int MAGICVAR = ...;
MAGICVAR[20][10] = 3; //sets the (200*20 + 10)th element


ACTUALIZACIÓN: Es importante mencionar esto: solo quiero tener un bloque de memoria contiguo. Simplemente no quiero escribir una macro como:

#define INDX(a,b) (a*200+b);

y luego referirse a mi blob como:

memory[INDX(a,b)];

Preferiría mucho:

memory[a][b];


ACTUALIZACIÓN: Entiendo que el compilador no tiene forma de saber tal cual. Estaría dispuesto a proporcionar información adicional, algo como:

int *MAGICVAR[][200] = memory;

¿No existe una sintaxis como esta? Tenga en cuenta que la razón por la que no solo uso una matriz de ancho fijo es que es demasiado grande para colocarla en la pila.


ACTUALIZACIÓN: OK chicos, puedo hacer esto:

void toldyou(char MAGICVAR[][286][5]) {
  //use MAGICVAR
}

//from another function:
  char *memory = (char *)malloc(sizeof(char)*1820*286*5);
  fool(memory);

Recibo una advertencia, passing arg 1 of toldyou from incompatible pointer type, pero el código funciona y he verificado que se accede a las mismas ubicaciones. ¿Hay alguna manera de hacer esto sin usar otra función?

  • Sí, esto ya se ha tratado muchas veces en SO, por ejemplo, Programación en C: malloc() para una matriz 2D (usando puntero a puntero)

    – Pablo R.

    29 de junio de 2010 a las 19:47


  • er lo siento, debo decir que no quiero tener punteros anidados. solo quiero un bloque contiguo de memoria.

    – claudio

    29 de junio de 2010 a las 19:49

  • Después de publicar mi respuesta, tenía la idea de “te dije” dando vueltas en mi cabeza. Simplemente no puedo imaginar cómo este poco de azúcar sintáctico vale todos los aros por los que tienes que pasar para conseguirlo;)

    – Rueda dentada

    29 de junio de 2010 a las 20:48

  • @Cogwheel: je, supongo que no lo es, ¡pero no lo sabía cuando publiqué esta pregunta por primera vez!

    – claudio

    29 de junio de 2010 a las 20:58

En la misma línea que la respuesta de Cogwheel, aquí hay un truco (algo sucio) que hace solo una llamada a malloc():

#define ROWS 400
#define COLS 200
int** array = malloc(ROWS * sizeof(int*) + ROWS * COLS * sizeof(int));
int i;
for (i = 0; i < ROWS; ++i)
    array[i] = (int*)(array + ROWS) + (i * COLS);

Esto llena la primera parte del búfer con punteros a cada fila en los datos de matriz contiguos inmediatamente siguientes.

  • Esto tiene la ventaja de funcionar incluso cuando no se conoce el tamaño de ninguna dimensión en tiempo de compilación. Ver también: c-faq.com/aryptr/dynmuldimary.html

    – jamesdlin

    30 de junio de 2010 a las 9:42

  • @jamesdlin Desde C99, las otras soluciones también funcionan cuando la dimensión no se conoce en tiempo de compilación; y esto tiene la desventaja de que la primera entrada en la matriz (después de la tabla de punteros) podría no estar alineada correctamente.

    –MM

    14/10/2014 a las 23:37

  • ¡Ah, sabía que era posible! tomen eso, detractores… @Tim: Lo siento, pero no me di cuenta de que su solución hizo lo que quería, caf lo hizo descaradamente obvio.

    – claudio

    30 de junio de 2010 a las 5:14

  • @Tim: Sí, también voté el tuyo cuando lo vi, pero pensé que también podría dejar mi respuesta, ya que parecía que solo éramos dos contra el mundo;)

    – café

    30 de junio de 2010 a las 5:24

  • @Claudiu: Probablemente valga la pena señalar que foo[] en las declaraciones de parámetros de función es solo azúcar sintáctico para (*foo) – es solo que [] significa algo diferente en las declaraciones de variables reales (donde significa una matriz cuyo tamaño está determinado por el inicializador).

    – café

    30 de junio de 2010 a las 5:26

  • ¡Puedes hacer aún más magia usando la función VLA de C99! int (*MOREMAGICVAR)[b] = (int (*)[b]) malloc(a * b * sizeof(int));

    – Vader B.

    27 de marzo de 2017 a las 16:22

  • @VaderB: Sí, la función VLA realmente solo es útil en declaraciones de variables como esa. Todavía puedes usar el a * sizeof MOREMAGICVAR[0] fórmula para el tamaño, por cierto (es decir, no repita b).

    – café

    28 de marzo de 2017 a las 0:19


avatar de usuario
tim schaeffer

Utilice un puntero a las matrices:

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

int main()
{
    int (*arr)[10];

    arr = malloc(10*10*sizeof(int));
    for (int i = 0; i < 10; i++)
        for(int j = 0; j < 10; j++)
            arr[i][j] = i*j;

    for (int i = 0; i < 10; i++)
        for(int j = 0; j < 10; j++)
            printf("%d\n", arr[i][j]);
    free(arr);
    return 0;
}

  • ¿Cómo libero () la memoria aquí?

    – generación

    24/10/2014 a las 20:36

#define ROWS 400
#define index_array_2d(a,i,j) (a)[(i)*ROWS + (j)]
...
index_array_2d( memory, 20, 10 ) = -1;
int x = index_array_2d( memory, 20, 10 );

Editar:

Matrices y punteros Mira muy parecidos, pero el compilador los trata de manera muy diferente. Veamos qué se debe hacer para indexar una matriz y eliminar la referencia de un puntero con desplazamiento:

  1. Digamos que declaramos una matriz estática (la matriz en la pila es solo un poco más complicada, el desplazamiento fijo de un registro, pero esencialmente lo mismo):

    static int array[10];

  2. Y un puntero:

    static int* pointer;

  3. Luego eliminamos la deferencia de cada uno de la siguiente manera:

    x = array[i];
    x = pointer[i];

Lo que hay que tener en cuenta es que la dirección del comienzo de arrayasí como, dirección de pointer (no su contenido) son reparado en el momento del enlace/carga. Luego, el compilador hace lo siguiente:

  1. Para array desreferenciar:
    • carga el valor de i,
    • se suma al valor de arrayes decir, su dirección fija, para formar la dirección de memoria de destino,
    • carga el valor de la dirección calculada
  2. Para pointer desreferenciar:
    • carga el valor de i,
    • carga el valor de pointeres decir, el contenido en su dirección,
    • suma dos valores para formar la dirección efectiva
    • carga el valor de la dirección calculada.

Lo mismo sucede con la matriz 2D con pasos adicionales para cargar el segundo índice y multiplicarlo por el tamaño de la fila (que es una constante). Todo esto se decide en tiempo de compilación, y no hay forma de sustituir uno por otro en tiempo de ejecución.

Editar:

@caf aquí tiene la solución correcta. Después de todo, hay una forma legal dentro del lenguaje de indexar un puntero como una matriz bidimensional.

  • ¿Cómo libero () la memoria aquí?

    – generación

    24/10/2014 a las 20:36

El compilador y el tiempo de ejecución no tienen forma de conocer las capacidades de dimensión previstas con solo una multiplicación en la llamada malloc.

Debe usar un puntero doble para lograr la capacidad de dos índices. Algo como esto debería hacerlo:

#define ROWS 400
#define COLS 200

int **memory = malloc(ROWS * sizeof(*memory));

int i;
for (i = 0; i < ROWS; ++i)
{
    memory[i] = malloc(COLS * sizeof(*memory[i]);
}

memory[20][10] = 3;

Asegúrese de verificar todos los valores de retorno de su malloc para obtener retornos NULL, lo que indica una falla en la asignación de memoria.

  • exactamente, pero ¿puedo contar de alguna manera? Hay muchas cosas en C que le puedes decir al compilador que haga, incluso si sabe que estás equivocado =P.

    – claudio

    29 de junio de 2010 a las 19:51

  • En ese caso, podría usar matrices de ancho fijo. :-PAG

    – Platino Azur

    29 de junio de 2010 a las 19:56

  • @Platinum: mal, ¡es demasiado grande! Consulte stackoverflow.com/questions/3144135/… . es por eso que tengo que malloc en primer lugar

    – claudio

    29 de junio de 2010 a las 20:01

  • Bueno, ese es mi punto entonces: a veces simplemente no puedes tener todo. Especialmente con un lenguaje de bajo nivel. No puede simplemente declarar que una matriz tenga un ancho fijo a menos que esté usando constantes de tiempo de compilación. Y si desea ALGUNOS beneficios del almacenamiento dinámico, o todos, debe trabajar dentro de las limitaciones del almacenamiento dinámico y el lenguaje. Apesta, lo sé, pero lo que quieres simplemente no es posible en ANSI C portátil.

    – Platino Azur

    29 de junio de 2010 a las 20:04

  • @Platinum: vea la última actualización, puedo hacer que funcione cuando paso cosas a una función … realmente no hay nada que restrinja inherentemente que el compilador haga esto. no es que la memoria en el montón sea intrínsecamente diferente a la memoria en la pila… pero puedo ver que esta función en particular probablemente no sea compatible.

    – claudio

    29 de junio de 2010 a las 20:16


¿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