Cómo asignar dinámicamente un bloque contiguo de memoria para una matriz 2D

5 minutos de lectura

avatar de usuario
choque de sueños

Si asigno una matriz 2D como esta en un[N][N]; asignará un bloque contiguo de memoria.

Pero si trato de hacerlo dinámicamente así:

int **a = malloc(rows * sizeof(int*));
for(int i = 0; i < rows; i++) 
   a[i] = malloc(cols * sizeof(int));

Esto mantiene una zancada unitaria entre los elementos de las filas, pero este puede no ser el caso entre filas.

Una solución es convertir de 2D a 1D, además de eso, ¿hay otra forma de hacerlo?

  • Esa no es una matriz multidimensional de C, es una matriz de punteros para separar filas. Utilice el puntero a un C99 VLA. ¿Una forma extraña de asignar una matriz bidimensional?

    – Peter Cordes

    31 de marzo de 2021 a las 14:19

Si las dimensiones de su matriz se conocen en el momento de la compilación:

#define ROWS ...
#define COLS ...

int (*arr)[COLS] = malloc(sizeof *arr * ROWS);
if (arr) 
{
  // do stuff with arr[i][j]
  free(arr);
}

Si las dimensiones de su matriz no se conocen en el momento de la compilación y está utilizando un compilador C99 o un compilador C2011 que admite matrices de longitud variable:

size_t rows, cols;
// assign rows and cols
int (*arr)[cols] = malloc(sizeof *arr * rows);
if (arr)
{
  // do stuff with arr[i][j]
  free(arr);
}

Si las dimensiones de su matriz no se conocen en el momento de la compilación y está no utilizando un compilador C99 o un compilador C2011 que admita matrices de longitud variable:

size_t rows, cols;
// assign rows and cols
int *arr = malloc(sizeof *arr * rows * cols);
{
  // do stuff with arr[i * rows + j]
  free(arr);
}

De hecho, los arreglos n-dimensionales (asignados en la pila) son en realidad vectores de 1 dimensión. La indexación múltiple es solo azúcar sintáctica. Pero puede escribir una función de acceso para emular algo como lo que desea:

int index_array(int *arr, size_t width, int x, int y)
{
    return arr[x * width + y];
}

const size_t width = 3;
const size_t height = 2;
int *arr = malloc(width * height * sizeof(*arr));

// ... fill it with values, then access it:

int arr_1_1 = index_array(arr, width, 1, 1);

Sin embargo, si tiene soporte para C99, entonces es posible declarar un puntero a una matriz, e incluso puede usar el azúcar sintáctico:

int (*arr)[width] = malloc(sizeof((*arr) * height);
arr[x][y] = 42;

avatar de usuario
matei tene

Supongamos que desea asignar dinámicamente una matriz de enteros bidimensional de filas ROWS y columnas COLS. Luego, primero puede asignar una porción continua de números enteros ROWS * COLS y luego dividirlo manualmente en filas ROWS. Sin azúcar sintáctico, esto dice

int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
for(int i = 0; i < ROWS; i++) 
   A[i] = mem + COLS*i;
// use A[i][j]

y se puede hacer de manera más eficiente evitando la multiplicación,

int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
A[0] = mem;
for(int i = 1; i < ROWS; i++) 
   A[i] = A[i-1] + COLS;
// use A[i][j]

Finalmente, uno podría renunciar por completo al puntero adicional,

int **A = malloc(ROWS * sizeof(int*));
A[0] = malloc(ROWS * COLS * sizeof(int));
for(int i = 1; i < ROWS; i++) 
   A[i] = A[i-1] + COLS;
// use A[i][j]

¡pero hay un GOTCHA importante! Debería tener cuidado de desasignar primero A[0] y luego A,

free(A[0]);
free(A);              // if this were done first, then A[0] would be invalidated

La misma idea se puede extender a arreglos de 3 o más dimensiones, aunque el código se complicará.

  • El código que usa esto todavía tiene que pasar por la indirección adicional de la matriz de punteros de fila. No recomendaría esto a menos que desee poder intercambiar filas reorganizando los elementos del puntero.

    – Peter Cordes

    31 de marzo de 2021 a las 14:43

  • Y por cierto, los compiladores normalmente harán la optimización de “evitar la multiplicación” por usted, haciendo tmp += COLS en lugar de i*COLS. en.wikipedia.org/wiki/Strength_reduction. Tu forma de escribirlo como A[i-1]+COLS también debería optimizar ok, pero no me molestaría.

    – Peter Cordes

    31 de marzo de 2021 a las 14:44

Puede tratar la memoria asignada dinámicamente como una matriz de un ninguna dimensión accediendo a ella en pasos:

int * a = malloc(sizeof(int) * N1 * N2 * N3);  // think "int[N1][N2][N3]"

a[i * N2 * N3 + j * N3 + k] = 10;              // like "a[i, j, k]"

avatar de usuario
Terremoto

Disculpe mi falta de formato o cualquier error, pero esto es de un teléfono celular.

También encontré avances en los que traté de usar fwrite() para generar resultados usando la variable int** como la dirección src.

Una solución fue hacer uso de dos invocaciones malloc():

#define HEIGHT 16
#define WIDTH 16

.
.
.
//allocate
int **data = malloc(HEIGHT * sizeof(int **));
int *realdata = malloc(HEIGHT * WIDTH * sizeof(int));

//manually index
for (int i = 0; i < HEIGHT; i++)
    data[i] = &realdata[i * WIDTH];

//populate
int idx = 0;
for (int i = 0; i < HEIGHT; i++)
    for (int j = 0; j < WIDTH; j++)
        data[i][j] = idx++;

//select
int idx = 0;
for (int i = 0; i < HEIGHT; i++)
{
    for (int j = 0; j < WIDTH; j++)
        printf("%i, ", data[i][j]);
    printf("/n");
}

//deallocate
.
.
.

Puede tipear su matriz (para menos headake) y luego hacer algo así:

#include <stdlib.h>
#define N 10
typedef int A[N][N];
int main () {
  A a; // on the stack
  a[0][0]=1;
  A *b=(A*)malloc (sizeof(A)); // on the heap
  (*b)[0][0]=1;
}

avatar de usuario
daniel pescador

La mejor manera es asignar un puntero a una matriz,

int (*a)[cols] = malloc(rows * sizeof *a);
if (a == NULL) {
    // alloc failure, handle or exit
}

for(int i = 0; i < rows; ++i) {
    for(int j = 0; j < cols; ++j) {
        a[i][j] = i+j;
    }
}

Si el compilador no admite matrices de longitud variable, eso solo funciona si cols es una expresión constante (pero entonces debería actualizar su compilador de todos modos).

  • Fraseo: esto es asignar espacio para una matriz 2D, o más simplemente “asignar una matriz 2D real”. los puntero a eso (a) es una variable local en el almacenamiento automático, por lo que no tiene que asignarse manualmente.

    – Peter Cordes

    31 de marzo de 2021 a las 14:38

¿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