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 un array en otra parte de mi código:

double anArray[10][10];

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

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

Author: Peter Mortensen, 2012-01-07

12 answers

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

  1. El parámetro es un array 2D

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. El parámetro es un array 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);
    
 339
Author: shengy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-25 18:38:23

Tamaño fijo

1. Pasar por referencia

template <size_t rows, size_t cols>
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 el array por referencia sin perder la información de la dimensión es probablemente lo más seguro, ya que uno no tiene que preocuparse de que el llamante pase una dimensión incorrecta (indicadores del compilador cuando no coinciden). Sin embargo, esto no es posible con matrices dinámicas (freestore); funciona para matrices automáticas (normalmente apilado-living) solo es decir, la dimensionalidad debe conocerse en compile tiempo.

2. Pasar por 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 C del método anterior es pasar la matriz por puntero. Esto no debe confundirse con pasar por el tipo de puntero decaído de la matriz(3), que es el método común y popular, aunque menos seguro que este, pero más flexible. Like (1), utilice este método cuando todas las dimensiones de la matriz son fijas y conocidas en tiempo de compilación. Tenga en cuenta que al llamar a la función la dirección de la matriz debe se pasa process_2d_array_pointer(&a) y no la dirección del primer elemento por decay process_2d_array_pointer(a).

Tamaño variable

Estos son heredados de C pero son menos seguros, el compilador no tiene forma de comprobar, garantizando que el llamador está pasando las dimensiones requeridas. La función solo se basa en lo que el llamante pasa como la(s) dimensión (s). Estos son más flexibles que los anteriores, ya que invariablemente se les pueden pasar matrices de diferentes longitudes.

Hay que recordar que no hay tal cosa como pasar una matriz directamente a una función en C [mientras que en C++ se pueden pasar como una referencia (1)]; (2) está pasando un puntero a la matriz y no la propia matriz. Siempre pasar una matriz tal cual se convierte en una operación de copia de puntero que se ve facilitada por la naturaleza de la matriz de descomponerse en un puntero.

3. Pasar por (value) 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 el sintaxis anterior ya que la sintaxis anterior deja claro que el identificador array es un único puntero a una matriz de 10 enteros, mientras que esta sintaxis parece como si fuera una matriz 2D pero es el mismo puntero a una matriz de 10 enteros. Aquí conocemos el número 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, debe pasarse como argumento. En este caso hay algo de seguridad ya que el compilador puede marcar cuando un puntero a una matriz con segundo se pasa la dimensión no igual a 10. La primera dimensión es la parte variable y se puede omitir. Vea aquí el razonamiento sobre por qué solo se permite omitir la primera dimensión.

4. Pasar por 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 sintaxis alternativa de int *array[10] que es la misma que int **array. En esta sintaxis el [10] es ignorado ya que decae en un puntero convirtiéndose así en int **array. Tal vez es solo una señal para la persona que llama que la matriz pasada debe tener al menos 10 columnas, incluso entonces se requiere el recuento de filas. En cualquier caso, el compilador no marca ninguna violación de longitud/tamaño (solo comprueba si el tipo pasado es un puntero a puntero), por lo tanto, requerir recuentos de filas y columnas como parámetro tiene sentido aquí.

Nota: (4) es la opción menos segura ya que apenas tiene ninguna comprobación de tipo y la más incómoda. Uno no puede legítimamente pasar una matriz 2D a esta función; C-FAQ condena el la solución habitual de hacer int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); ya que puede potencialmente conducir a un comportamiento indefinido debido al aplanamiento de matrices. 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, a ser pasada; esta sustituta se pasa a la función (ver más abajo); todo esto para hacer el mismo trabajo que los métodos anteriores, que son más seguros, más limpio y quizás más rápido.

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

#include <iostream>

// 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**
}
 132
Author: legends2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

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 gestionarse y eliminarse):

template <size_t size_x, size_t size_y>
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 print están ahí para mostrar que los arrays se pasan por referencia (mostrando las direcciones de las variables)

 34
Author: Zrax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-27 19:21:04

Puede crear una plantilla de función como esta:

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

Entonces tiene ambos tamaños de dimensión a través de 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. Puedes usarlo como un envoltorio sobre una función como esta:

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

Trata la matriz como unidimensional, y utiliza la aritmética para calcular las compensaciones de los índices. En este caso, lo haría define la plantilla así:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
 19
Author: Benjamin Lindley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-07 04:20:20

anArray[10][10] no es un puntero a un puntero, es un trozo de memoria contiguo adecuado para almacenar 100 valores de tipo double, cuyo compilador sabe cómo direccionar porque especificó las dimensiones. Es necesario 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<std::vector<double> >: es casi tan eficiente, y significativamente más conveniente.

 10
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-07 03:54:05

Sorprendido de que nadie mencionó esto todavía, pero simplemente se puede plantilla en cualquier 2D apoyo semántica [] [].

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

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

 9
Author: LemonPi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-22 18:26:25

El array unidimensional decae a un puntero puntero apuntando al primer elemento del array. Mientras que una matriz 2D decae a un puntero que apunta a la primera fila. Por lo tanto, el prototipo de la función debe ser -

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

Preferiría std::vector sobre matrices raw.

 7
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-07 03:54:54

, puedes hacer algo como esto...

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

Su salida será la siguiente...

11.5  12.5
13.5  14.5
 7
Author: Sagar Shah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-20 14:57:13

Una cosa importante para pasar matrices multidimensionales es:

  • First array dimension no es necesario especificar.
  • Second(any any further)dimension debe especificarse.

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 solo puntero: En este método,debemos encasillar la matriz 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;
}`
 1
Author: sonorous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-10 04:41:29

Aquí hay un vector de matriz de vectores ejemplo

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(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
 1
Author: edW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-07 19:21:20

Puede utilizar template facility en C++ para hacer esto. Hice algo como esto:

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

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

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

Crea una instancia de la plantilla dos veces para producir 2 definiciones de función (una donde col = 3 y otra donde col = 5).

 0
Author: vantony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-22 18:51:21

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

  • Usando single pointer tenemos que encasillar el array 2D.

    #include<bits/stdc++.h>
    using namespace std;
    
    
    void func(int *arr, int m, int n)
    {
        for (int i=0; i<m; i++)
        {
           for (int j=0; j<n; j++)
           {
              cout<<*((arr+i*n) + j)<<" ";
           }
           cout<<endl;
        }
    }
    
    int main()
    {
        int m = 3, n = 3;
        int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        func((int *)arr, m, n);
        return 0;
    }
    
  • Usando double pointer De esta manera, también encasillamos el array 2d

    #include<bits/stdc++.h>
    using namespace std;

   void func(int **arr, int row, int col)
   {
      for (int i=0; i<row; i++)
      {
         for(int j=0 ; j<col; j++)
         {
           cout<<arr[i][j]<<" ";
         }
         printf("\n");
      }
   }

  int main()
  {
     int row, colum;
     cin>>row>>colum;
     int** arr = new int*[row];

     for(int i=0; i<row; i++)
     {
        arr[i] = new int[colum];
     }

     for(int i=0; i<row; i++)
     {
         for(int j=0; j<colum; j++)
         {
            cin>>arr[i][j];
         }
     }
     func(arr, row, colum);

     return 0;
   }
 0
Author: rashedcs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 11:51:54