Pasar una matriz de longitud variable multidimensional a una función
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.
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_array
es 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 B
fila. 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.
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);
}