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

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

¿Cómo pasar la 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 comstackdor ( 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 comstackdor aún 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, generalmente no desea que se copie toda la matriz cuando la pasa a una función.

Con arrays de longitud constante es simple, ya que, como lo implica la “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); } 

Sé que pasar arreglos a funciones como esta no es una mejor práctica, y no me gusta para nada. Probablemente sea mejor hacerlo con punteros planos u objetos (como std: vector) o de alguna otra manera. Pero aún así, soy un poco curioso, ¿cuál es la respuesta aquí desde un punto de vista teórico?

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

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

  • el puntero hacia donde comienza
  • cuán amplia es una fila

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

Algunas formas de escribir eso:

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 w / argumentos invertidos, statement de parámetros hacia delante (extensión GNU C)

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

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

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

Gran comentario:

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

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

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

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

  • estás en el verde en C,
  • todavía necesitas un desastre 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 hacer dos asignaciones separadas:

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

Luego ajuste los punteros en (A) para apuntar a las respectivas filas de (B).

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

Esta solución es buena para 2D, pero necesita más y más repetitivo para dimensiones más grandes. También es más lento que la solución VLA debido a la capa adicional de indirección.

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

No existe una forma clara de hacerlo, pero puede usar 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); }