¿Cómo pasar una matriz 2D (matriz) en una función en C?

6 minutos de lectura

avatar de usuario
Shweta

Necesito hacer esto para persistir las operaciones en la matriz también. ¿Significa eso que debe pasarse por referencia?

¿Será esto suficiente?

void operate_on_matrix(char matrix[][20]);

avatar de usuario
Bart van Ingen Schenau

C realmente no tiene matrices multidimensionales, pero hay varias formas de simularlas. La forma de pasar dichas matrices a una función depende de la forma utilizada para simular las múltiples dimensiones:

1) Usar una matriz de matrices. Esto solo se puede usar si los límites de su matriz están completamente determinados en el momento de la compilación, o si su compilador admite VLA:

#define ROWS 4
#define COLS 5

void func(int array[ROWS][COLS])
{
  int i, j;

  for (i=0; i<ROWS; i++)
  {
    for (j=0; j<COLS; j++)
    {
      array[i][j] = i*j;
    }
  }
}

void func_vla(int rows, int cols, int array[rows][cols])
{
  int i, j;

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

int main()
{
  int x[ROWS][COLS];

  func(x);
  func_vla(ROWS, COLS, x);
}

2) Use una matriz (asignada dinámicamente) de punteros a matrices (asignadas dinámicamente). Esto se usa principalmente cuando los límites de la matriz no se conocen hasta el tiempo de ejecución.

void func(int** array, int rows, int cols)
{
  int i, j;

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

int main()
{
  int rows, cols, i;
  int **x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * sizeof *x);
  for (i=0; i<rows; i++)
  {
    x[i] = malloc(cols * sizeof *x[i]);
  }

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  for (i=0; i<rows; i++)
  {
    free(x[i]);
  }
  free(x);
}

3) Use una matriz unidimensional y corrija los índices. Esto se puede usar con arreglos asignados estáticamente (tamaño fijo) y asignados dinámicamente:

void func(int* array, int rows, int cols)
{
  int i, j;

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

int main()
{
  int rows, cols;
  int *x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * cols * sizeof *x);

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  free(x);
}

4) Utilice un VLA asignado dinámicamente. Una ventaja de esto sobre la opción 2 es que hay una sola asignación de memoria; otra es que se necesita menos memoria porque no se requiere la matriz de punteros.

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

extern void func_vla(int rows, int cols, int array[rows][cols]);
extern void get_rows_cols(int *rows, int *cols);
extern void dump_array(const char *tag, int rows, int cols, int array[rows][cols]);

void func_vla(int rows, int cols, int array[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            array[i][j] = (i + 1) * (j + 1);
        }
    }
}

int main(void)
{
    int rows, cols;

    get_rows_cols(&rows, &cols);

    int (*array)[cols] = malloc(rows * cols * sizeof(array[0][0]));
    /* error check omitted */

    func_vla(rows, cols, array);
    dump_array("After initialization", rows, cols, array);

    free(array);
    return 0;
}

void dump_array(const char *tag, int rows, int cols, int array[rows][cols])
{
    printf("%s (%dx%d):\n", tag, rows, cols);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
            printf("%4d", array[i][j]);
        putchar('\n');
    }
}

void get_rows_cols(int *rows, int *cols)
{
    srand(time(0));           // Only acceptable because it is called once
    *rows = 5 + rand() % 10;
    *cols = 3 + rand() % 12;
}

(Ver srand() — ¿Por qué llamarlo solo una vez?.)

  • De la primera manera mencionada anteriormente, el código no se compilará. “rows” y “cols” en las líneas 17 y 35 deben cambiar a “ROWS” y “COLS”, respectivamente.

    – KZcodificación

    27 mayo 2015 a las 15:35

  • void func_vla(int array[rows][cols], int rows, int cols) debiera ser void func_vla(int rows, int cols, int array[rows][cols])

    –David Ranieri

    12/10/2015 a las 16:55

  • @KZcoding: la notación VLA utilizada en las líneas 17 y 35 es correcta si el compilador admite C99 o si admite C11 y no define __STDC_NO_VLA__. Si el compilador no admite VLA, entonces, por supuesto, no compila.

    –Jonathan Leffler

    26 de diciembre de 2018 a las 16:45

  • malloc devuelve un puntero vacío, ¿está seguro de que no necesita convertirlo en int* o int** cuando sea necesario? Solución 2

    – CocoCrisp

    5 de marzo de 2019 a las 19:22

  • Casting es más una convención de C++, ¡entendido! Gracias @BartvanIngenSchenau

    – CocoCrisp

    5 de marzo de 2019 a las 20:16

avatar de usuario
Minhas Kamal

La forma más fácil de pasar una matriz 2D de longitud variable

La técnica más limpia para C y C ++ es: pasar una matriz 2D como una matriz 1D, luego usarla como 2D dentro de la función.

#include <stdio.h>

void func(int row, int col, int* matrix){
    int i, j;
    for(i=0; i<row; i++){
        for(j=0; j<col; j++){
            printf("%d ", *(matrix + i*col + j)); // or better: printf("%d ", *matrix++);
        }
        printf("\n");
    }
}

int main(){
    int matrix[2][3] = { {0, 1, 2}, {3, 4, 5} };
    func(2, 3, matrix[0]);

    return 0;
}

Internamente, no importa cuántas dimensiones tenga una matriz, C/C++ siempre mantiene una matriz 1D. Y así, podemos pasar cualquier matriz multidimensional como esta.

  • Solo una pequeña consulta: si llamamos a la función como func(2, 3, matrix)entonces deberíamos tener void func(int row, int col, int** matrix)?

    – Kartik Chhajed

    7 sep 2020 a las 13:36


  • @KartikChhajed Su código requiere una conversión implícita de int*[3] para int**que podría no estar permitido para la mayoría de los compiladores de C/C++.

    – Minhas Kamal

    24 de octubre de 2020 a las 5:55


  • @jwpol lamentablemente no es una buena idea: stackoverflow.com/q/25303647/1606345

    –David Ranieri

    24 de octubre de 2020 a las 7:55

  • @David Ranieri Si conoce la dimensión de la matriz, no veo ninguna razón por la que no deba eliminar la referencia a un elemento en particular. Por ejemploint matrix[2][2]ocupa 4 “celdas” de memoria, por lo que desde *(p) para *(p+3) (donde p es una dirección del primer elemento) están dentro de la dimensión de la memoria asignada. Lo mismo en esta respuesta. El hilo que publicaste trata sobre la desreferenciación de un elemento después del último en la matriz, lo cual no es el caso aquí. La respuesta proporcionada por @Minhas Kamal es absolutamente segura siempre que pase límites legítimos en raw y col.

    – jwpol

    24 oct 2020 a las 15:08

  • evaluación de *(matrix + i*col + j) por i/j igual a 0/1 invoca UB. El tipo de puntero de objeto por matrix es int[2]. Por lo tanto *(matrix + 2) Es incorrecto

    – tstanisl

    10 dic 2021 a las 11:51

matriz 2D:

int sum(int array[][COLS], int rows)
{

}

matriz 3D:

int sum(int array[][B][C], int A)
{

}

matriz 4D:

int sum(int array[][B][C][D], int A)
{

}

y matriz nD:

int sum(int ar[][B][C][D][E][F].....[N], int A)
{

}

No sé a qué te refieres con “los datos no se pierden”. Así es como pasa una matriz 2D normal a una función:

void myfunc(int arr[M][N]) { // M is optional, but N is required
  ..
}

int main() {
  int somearr[M][N];
  ...
  myfunc(somearr);
  ...
}

¿Ha sido útil esta solución?