Pasar una matriz 2D a una función C ++

Tengo una función que quiero tomar, como parámetro, una matriz 2D de tamaño variable.

Hasta ahora tengo esto:

void myFunction(double** myArray){ myArray[x][y] = 5; etc... } 

Y he declarado una matriz en otro lugar de mi código:

 double anArray[10][10]; 

Sin embargo, llamar a myFunction(anArray) me da un error.

No quiero copiar la matriz cuando la paso. Cualquier cambio realizado en myFunction debe alterar el estado de una anArray . Si entiendo correctamente, solo quiero pasar como argumento un puntero a una matriz 2D. La función necesita aceptar matrices de diferentes tamaños también. Entonces, por ejemplo, [10][10] y [5][5] . ¿Cómo puedo hacer esto?

Hay tres formas de pasar una matriz 2D a una función:

  1. El parámetro es una matriz 2D

     int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array); 
  2. El parámetro es una matriz que contiene punteros

     int *array[10]; for(int i = 0; i < 10; i++) array[i] = new int[10]; void passFunc(int *a[10]) //Array containing pointers { // ... } passFunc(array); 
  3. El parámetro es un puntero a un puntero

     int **array; array = new int *[10]; for(int i = 0; i <10; i++) array[i] = new int[10]; void passFunc(int **a) { // ... } passFunc(array); 

Tamaño fijo

1. Pase por referencia

 template  void process_2d_array_template(int (&array)[rows][cols]) { std::cout < < __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < cols; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

En C ++, pasar la matriz por referencia sin perder la información de la dimensión es probablemente la más segura, ya que no es necesario preocuparse de que la persona que llama pase una dimensión incorrecta (el comstackdor marca cuando no coinciden). Sin embargo, esto no es posible con matrices dinámicas (freestore); funciona solo para matrices automáticas ( generalmente de vida en stack ), es decir, la dimensionalidad debe conocerse en tiempo de comstackción.

2. Pase por el puntero

 void process_2d_array_pointer(int (*array)[5][10]) { std::cout < < __func__ << std::endl; for (size_t i = 0; i < 5; ++i) { std::cout << i << ": "; for (size_t j = 0; j < 10; ++j) std::cout << (*array)[i][j] << '\t'; std::cout << std::endl; } } 

El equivalente en C del método anterior pasa el conjunto por puntero. Esto no se debe confundir con pasar por el tipo de puntero decaído (3) de la matriz, que es el método común y popular, aunque menos seguro que este pero más flexible. Al igual que (1) , utilice este método cuando todas las dimensiones de la matriz son fijas y conocidas en tiempo de comstackción. Tenga en cuenta que al llamar a la función, la dirección de la matriz debe pasarse process_2d_array_pointer(&a) y no la dirección del primer elemento mediante decay process_2d_array_pointer(a) .

Tamaño variable

Estos son heredados de C pero son menos seguros, el comstackdor no tiene manera de verificar, lo que garantiza que la persona que llama está pasando las dimensiones requeridas. La función solo se basa en lo que la persona que llama transfiere como dimensión (es). Estos son más flexibles que los anteriores, ya que los arrays de diferentes longitudes pueden pasarse a ellos invariablemente.

Debe recordarse que no existe la posibilidad de pasar una matriz directamente a una función en C [mientras que en C ++ se pueden pasar como referencia (1) ]; (2) está pasando un puntero a la matriz y no la matriz en sí. Pasar siempre una matriz como está se convierte en una operación de copia de puntero que se ve facilitada por la naturaleza de decaimiento de la matriz en un puntero .

3. Pase por (valor) un puntero al tipo decaído

 // int array[][10] is just fancy notation for the same thing void process_2d_array(int (*array)[10], size_t rows) { std::cout < < __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < 10; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

Aunque int array[][10] está permitido, no lo recomendaría sobre la syntax anterior ya que la syntax anterior deja en claro que la array identificador es un solo puntero a una matriz de 10 enteros, mientras que esta syntax parece ser una 2D array pero es el mismo puntero a una matriz de 10 enteros. Aquí conocemos la cantidad de elementos en una sola fila (es decir, el tamaño de la columna, 10 aquí) pero el número de filas es desconocido y, por lo tanto, se pasa como un argumento. En este caso hay algo de seguridad ya que el comstackdor puede marcar cuando se pasa un puntero a una matriz con una segunda dimensión no igual a 10. La primera dimensión es la parte variable y se puede omitir. Vea aquí la razón de por qué solo se permite omitir la primera dimensión.

4. Pase por el puntero a un puntero

 // int *array[10] is just fancy notation for the same thing void process_pointer_2_pointer(int **array, size_t rows, size_t cols) { std::cout < < __func__ << std::endl; for (size_t i = 0; i < rows; ++i) { std::cout << i << ": "; for (size_t j = 0; j < cols; ++j) std::cout << array[i][j] << '\t'; std::cout << std::endl; } } 

Nuevamente hay una syntax alternativa de int *array[10] que es lo mismo que int **array . En esta syntax, [10] se ignora, ya que se descompone en un puntero convirtiéndose en int **array . Tal vez sea solo una señal para la persona que llama que la matriz pasada debe tener al menos 10 columnas, incluso cuando se requiera el recuento de filas. En cualquier caso, el comstackdor no marca las violaciones de longitud / tamaño (solo comprueba si el tipo pasado es un puntero a puntero), por lo tanto, se requieren tanto recuentos de fila como de columna, ya que el parámetro tiene sentido aquí.

Nota: (4) es la opción menos segura ya que casi no tiene ningún tipo de verificación y es la más inconveniente. Uno no puede pasar legítimamente una matriz 2D a esta función; C-FAQ condena la solución habitual de hacer int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); ya que puede conducir a un comportamiento indefinido debido al aplanamiento de la matriz. La forma correcta de pasar una matriz en este método nos lleva a la parte inconveniente, es decir, necesitamos una matriz adicional (sustituta) de punteros con cada uno de sus elementos apuntando a la fila respectiva de la matriz real que se va a pasar; este sustituto se pasa luego a la función (ver a continuación); todo esto para hacer el mismo trabajo que los métodos anteriores que son más seguros, más limpios y quizás más rápidos.

Aquí hay un progtwig de controlador para probar las funciones anteriores:

 #include  // copy above functions here int main() { int a[5][10] = { { } }; process_2d_array_template(a); process_2d_array_pointer(&a); // < -- notice the unusual usage of addressof (&) operator on an array process_2d_array(a, 5); // works since a's first dimension decays into a pointer thereby becoming int (*)[10] int *b[5]; // surrogate for (size_t i = 0; i < 5; ++i) { b[i] = a[i]; } // another popular way to define b: here the 2D arrays dims may be non-const, runtime var // int **b = new int*[5]; // for (size_t i = 0; i < 5; ++i) b[i] = new int[10]; process_pointer_2_pointer(b, 5, 10); // process_2d_array(b, 5); // doesn't work since b's first dimension decays into a pointer thereby becoming int** } 

Una modificación a la primera sugerencia de shengy, puede usar plantillas para hacer que la función acepte una variable de matriz multidimensional (en lugar de almacenar una matriz de punteros que deben ser administrados y eliminados):

 template  void func(double (&arr)[size_x][size_y]) { printf("%p\n", &arr); } int main() { double a1[10][10]; double a2[5][5]; printf("%p\n%p\n\n", &a1, &a2); func(a1); func(a2); return 0; } 

Las instrucciones de impresión están ahí para mostrar que las matrices se pasan por referencia (al mostrar las direcciones de las variables)

Puede crear una plantilla de función como esta:

 template void myFunction(double (&myArray)[R][C]) { myArray[x][y] = 5; etc... } 

Luego tiene ambos tamaños de dimensión mediante R y C. Se creará una función diferente para cada tamaño de matriz, por lo que si su función es grande y la llama con una variedad de tamaños de matriz diferentes, esto puede ser costoso. Podrías usarlo como un contenedor sobre una función como esta:

 void myFunction(double * arr, int R, int C) { arr[x * C + y] = 5; etc... } 

Trata la matriz como una dimensión y usa la aritmética para determinar las compensaciones de los índices. En este caso, definiría la plantilla así:

 template void myFunction(double (&myArray)[R][C]) { myFunction(*myArray, R, C); } 

anArray[10][10] no es un puntero a un puntero, es un trozo contiguo de memoria adecuado para almacenar 100 valores de tipo double, que el comstackdor sabe cómo abordar porque ha especificado las dimensiones. Necesita pasarlo a una función como una matriz. Puede omitir el tamaño de la dimensión inicial, de la siguiente manera:

 void f(double p[][10]) { } 

Sin embargo, esto no le permitirá pasar matrices con la última dimensión que no sea diez.

La mejor solución en C ++ es usar std::vector > : es casi igual de eficiente y significativamente más conveniente.

Sorprendido de que nadie haya mencionado esto todavía, pero puede simplemente crear plantillas en cualquier semántica que soporte [] [].

 template  void myFunction(TwoD& myArray){ myArray[x][y] = 5; etc... } // call with double anArray[10][10]; myFunction(anArray); 

Funciona con cualquier estructura de datos 2D tipo “matriz”, como std::vector> , o un tipo definido por el usuario para maximizar la reutilización del código.

La matriz unidimensional se desintegra a un puntero que apunta al primer elemento de la matriz. Mientras que una matriz 2D se descompone en un puntero que apunta a la primera fila. Entonces, el prototipo de la función debería ser:

 void myFunction(double (*myArray) [10]); 

Preferiría std::vector sobre matrices en bruto.

Puedes hacer algo como esto …

 #include using namespace std; //for changing values in 2D array void myFunc(double *a,int rows,int cols){ for(int i=0;i 

Su salida será la siguiente ...

 11.5 12.5 13.5 14.5 

Una cosa importante para pasar arrays multidimensionales es:

  • First array dimension no necesita ser especificada.
  • Second(any any further)dimension debe especificar una Second(any any further)dimension .

1. Cuando solo la segunda dimensión está disponible globalmente (ya sea como una macro o como una constante global)

 `const int N = 3; `void print(int arr[][N], int m) { int i, j; for (i = 0; i < m; i++) for (j = 0; j < N; j++) printf("%d ", arr[i][j]); }` int main() { int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; print(arr, 3); return 0; }` 

2. Usando un único puntero : En este método, debemos convertir el arreglo en 2D al pasar a la función.

 `void print(int *arr, int m, int n) { int i, j; for (i = 0; i < m; i++) for (j = 0; j < n; j++) printf("%d ", *((arr+i*n) + j)); } `int main() { int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int m = 3, n = 3; // We can also use "print(&arr[0][0], m, n);" print((int *)arr, m, n); return 0; }` 

Aquí hay un vector de ejemplo de matriz de vectores

 #include  #include  using namespace std; typedef vector< vector > Matrix; void print(Matrix& m) { int M=m.size(); int N=m[0].size(); for(int i=0; i(4,0)); print(n); return 0; } 

salida:

 1 2 3 4 5 6 7 8 9 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 

Puede usar el recurso de plantilla en C ++ para hacer esto. Hice algo como esto:

 template T process(T a[][col], size_t row) { ... } 

El problema con este enfoque es que para cada valor de col que proporcione, se crea una nueva definición de función utilizando la plantilla. asi que,

 int some_mat[3][3], another_mat[4,5]; process(some_mat, 3); process(another_mat, 4); 

instancia la plantilla dos veces para producir 2 definiciones de funciones (una donde col = 3 y una donde col = 5).

Podemos usar varias formas de pasar una matriz 2D a una función:

  • Usando un solo puntero tenemos que encasillar el arreglo 2D.

     #include using namespace std; void func(int *arr, int m, int n) { for (int i=0; i 
  • Usando el puntero doble De esta manera, también encasillamos la matriz 2d

  #include using namespace std; void func(int **arr, int row, int col) { for (int i=0; i>row>>colum; int** arr = new int*[row]; for(int i=0; i>arr[i][j]; } } func(arr, row, colum); return 0; }