Pasar una matriz de longitud variable multidimensional a una función

Resuelto alveko asked hace 11 años • 2 respuestas

Hay toneladas de preguntas similares, pero todavía no pude encontrar ninguna respuesta relevante para la característica de matrices de longitud variable en C99/C11.

¿ Cómo pasar una matriz de longitud variable multidimensional a una función en C99/C11?

Por ejemplo:

void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}

void bar(int n)
{
    int arr[n][n];
    foo(n, arr);
}

El compilador ( g++-4.7 -std=gnu++11) dice:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first

Si lo cambio a int *arr[], el compilador todavía se queja:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’

Siguiente pregunta, ¿cómo pasarlo por valor y cómo pasarlo por referencia? Aparentemente, normalmente no desea que se copie toda la matriz cuando la pasa a una función.

Con matrices de longitud constante es simple, ya que, como implica "constante", debes saber la longitud cuando declaras la función:

void foo2(int n, int arr[][10]) // <-- ok
{
}

void bar2()
{
    int arr[10][10];
    foo2(10, arr);
}

Lo sé, pasar matrices a funciones como esta no es una buena práctica y no me gusta en absoluto. Probablemente sea mejor hacerlo con punteros planos u objetos (como std:vector) o de alguna otra manera. Pero aún así, tengo un poco de curiosidad por saber cuál es la respuesta aquí desde un punto de vista teórico.

alveko avatar Jan 27 '13 22:01 alveko
Aceptado

Pasar matrices a funciones es un poco divertido en C y C++. No hay valores de tipos de matriz, por lo que en realidad estás pasando un puntero.

Para abordar una matriz 2D (una real, no una matriz de matrices), deberá pasar 2 fragmentos de datos:

  • el puntero a donde comienza
  • ¿Qué tan ancha es una fila?

Y estos son dos valores separados, ya sea C o C++ o con VLA o sin él o lo que sea.

Algunas formas de escribir eso:

El más simple, funciona en todas partes pero necesita más trabajo manual.

void foo(int width, int* arr) {
    arr[x + y*width] = 5;
}

VLA, estándar C99

void foo(int width, int arr[][width]) {
    arr[x][y] = 5;
}

VLA con argumentos invertidos, declaración de parámetros directos (extensión GNU C)

void foo(int width; int arr[][width], int width) {
    arr[x][y]=5;
}

C++ con VLA (extensión GNU C++, terriblemente fea)

void foo(int width, int* ptr) {
    typedef int arrtype[][width];
    arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
    arr[x][y]=5;
}

Gran comentario:

La notación [x][y] con una matriz 2D funciona porque el tipo de matriz contiene el ancho. No VLA = los tipos de matriz deben corregirse en tiempo de compilación.

Por lo tanto: si no puedes usar VLA, entonces...

  • no hay manera de manejarlo en C,
  • no hay forma de manejarlo sin una clase de proxy con operador sobrecargado en C++.

Si puede usar VLA (extensiones C99 o GNU C++), entonces...

  • estás en verde en C,
  • Aún necesitas un lío en C++, usa clases en su lugar.

Para C++, boost::multi_arrayes una opción sólida.

Una solución

Para matrices 2D, puede realizar dos asignaciones independientes:

  • una matriz 1D de punteros a T(A)
  • una matriz 2D de T(B)

Luego configure los punteros en (A) para que apunten a las filas respectivas de (B).

Con esta configuración, puede pasar (A) como simple T**y se comportará bien con [x][y]la indexación.

Esta solución es buena para 2D, pero necesita cada vez más texto estándar para dimensiones más altas. También es más lenta que la solución VLA debido a la capa adicional de direccionamiento indirecto.

También puede encontrarse con una solución similar con una asignación separada para cada Bfila. En C, esto parece un malloc-in-a-loop y es análogo al vector de vectores de C++. Sin embargo, esto elimina el beneficio de tener toda la matriz en un solo bloque.

Kos avatar Jan 27 '2013 15:01 Kos

No existe una forma clara de hacer esto, pero puede utilizar una solución alternativa para tratar una matriz bidimensional como una matriz unidimensional y luego reconvertirla en una matriz bidimensional dentro de la función.

void foo2(int n, int *arr) 
{
    int *ptr; // use this as a marker to go to next block
    int i;
    int j;

    for(i = 0; i < n; i++)
    {
        ptr = arr + i*n; // this is the starting for arr[i] ...
        for (j = 0; j < n ;j++)
        {
            printf(" %d ", ptr[j]); // This is same as arr[i][j]
        }
    }
}

void bar2()
{
    int arr[10][10];
    foo2(10, (int *)arr);
}
Pankaj Rai avatar Nov 28 '2016 00:11 Pankaj Rai