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

13 minutos de lectura

Pasar una matriz 2D a una funcion C
RogerDarwin

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

double anArray[10][10];

Sin embargo, llamar myFunction(anArray) me da error

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

  • no se puede convertir el parámetro 3 de ‘doble [10][10]’ para duplicar **’

    – RogerDarwin

    7 de enero de 2012 a las 3:43

  • La respuesta aceptada muestra solo 2 técnicas. [its (2) and (3) are the same] pero hay 4 formas únicas de pasar una matriz 2D a una función.

    – leyendas2k

    24 de marzo de 2014 a las 11:44


  • Estrictamente hablando, sí, no son matrices 2D, pero esta convención (aunque conduce a UB) de tener una matriz de punteros, cada uno apuntando a una matriz (1D), parece prevalecer 🙁 Tener una matriz 1D aplanada de mxn longitud, con funciones auxiliares/clase para emular una matriz 2D es quizás mejor.

    – leyendas2k

    12 de julio de 2015 a las 8:40

  • MÁS FÁCILfunc(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }. Llámalo como- int mat[3][5]; func(mat[0], 3, 5);

    – Minhas Kamal

    9 de enero de 2019 a las 4:24


1647643271 913 Pasar una matriz 2D a una funcion C
shengy

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);
    

  • @Overflowh Puedes obtener los elementos de array con array[i][j] 🙂

    – shengy

    27 de mayo de 2013 a las 3:15

  • Para el primer caso, el parámetro se puede declarar como int (*a)[10].

    – Zachary

    13 de junio de 2013 a las 14:38

  • Para el segundo caso, el parámetro se puede declarar como int **.

    – Zachary

    14 de junio de 2013 a las 3:14

  • agregaría un 4. usando un vector<vector<int>>

    – 463035818_no_es_un_número

    5 mayo 2015 a las 14:59

  • Los casos 2 y 3 no son matrices 2D, por lo que esta respuesta es engañosa. Mira esto.

    – Lundin

    16 de junio de 2015 a las 8:05

Pasar una matriz 2D a una funcion C
leyendas2k

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 la matriz por referencia sin perder la información de la dimensión es probablemente lo más seguro, ya que uno no necesita preocuparse de que la persona que llama pase una dimensión incorrecta (marcas del compilador cuando no coinciden). Sin embargo, esto no es posible con matrices dinámicas (freestore); funciona solo para arreglos automáticos (generalmente con vida de pila), es decir, la dimensionalidad debe conocerse en el momento de la compilación.

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 en 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. Me gusta (1), use este método cuando todas las dimensiones de la matriz sean fijas y conocidas en tiempo de compilación. Tenga en cuenta que al llamar a la función, se debe pasar la dirección de la matriz process_2d_array_pointer(&a) y no la dirección del primer elemento por decaimiento process_2d_array_pointer(a).

Tamaño variable

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

Debe recordarse que no existe tal cosa como pasar una matriz directamente a una función en C [while in C++ they can be passed as a reference (1)]; (2) está pasando un puntero a la matriz y no a la matriz en sí. Pasar siempre una matriz tal como está se convierte en una operación de copia de puntero que se ve facilitada por la naturaleza de la matriz de decaer 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 sintaxis anterior ya que la sintaxis anterior deja en claro que el identificador array es un solo puntero a una matriz de 10 enteros, mientras que esta sintaxis mira 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 se pasa un puntero a una matriz con una segunda dimensión que no es igual a 10. La primera dimensión es la parte variable y se puede omitir. Vea aquí la justificación de por qué solo se permite omitir la primera dimensión.

4. Pasar de puntero a 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 lo mismo que int **array. En esta sintaxis el [10] se ignora a medida que decae en un puntero convirtiéndose así en int **array. Tal vez sea solo una señal para la persona que llama de 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 verifica 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 comprobacion de tipo y lo mas 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); ya que potencialmente 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 luego 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 limpios y quizás más rápidos.

Aquí hay un programa 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**
}

  • ¿Qué hay de pasar matrices asignadas dinámicamente a funciones en C++? En el estándar C11, se puede hacer para matrices asignadas de forma estática y dinámica como esa fn (int col, int row, int array[col][row]): stackoverflow.com/questions/16004668/… Hice la pregunta para este problema: stackoverflow.com/questions/27457076/…

    – 42n4

    13 de diciembre de 2014 a las 9:16


  • @42n4 Caso 4 cubre (también para C++) eso. Para arreglos asignados dinámicamente, solo la línea dentro del bucle cambiaría de b[i] = a[i]; decir, b[i] = new int[10];. También se puede hacer b asignado dinámicamente int **b = int *[5]; y seguirá funcionando como está.

    – leyendas2k

    15 de diciembre de 2014 a las 6:42

  • ¿Cómo se dirige array[i][j] trabajar en la función en 4)? Debido a que ha recibido ptr a ptr y no conoce el valor de la última dimensión, ¿cuál es necesario para realizar un cambio para el direccionamiento correcto?

    – usuario1234567

    16 de diciembre de 2014 a las 16:27

  • array[i][j] es solo aritmética de punteros, es decir, al valor del puntero arrayagregaría i y desreferenciar el resultado como int*a lo que se sumaría j y desreferenciar esa ubicación, leyendo un int. Entonces, no, no necesita conocer ninguna dimensión para esto. Pero, ¡ese es el punto! El compilador toma la palabra del programador con fe y si el programador fue incorrecto, se produce un comportamiento indefinido. Esta es la razón por la que mencioné que el caso 4 es la opción menos segura.

    – leyendas2k

    17 de diciembre de 2014 a las 3:04


  • En tales casos, una estructura puede servirle bien.

    – Xofo

    16 de noviembre de 2018 a las 18:17

1647643272 840 Pasar una matriz 2D a una funcion C
Zrax

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 administrarse 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 declaraciones de impresión están ahí para mostrar que las matrices se pasan por referencia (al mostrar las direcciones de las variables)

  • Deberías usar %p para imprimir un puntero, e incluso entonces, debe convertirlo en void *demás printf() invoca un comportamiento indefinido. Además, no debe utilizar la dirección de (&) al llamar a las funciones, ya que las funciones esperan un argumento de tipo double (*)[size_y]mientras que actualmente los pasas double (*)[10][10] y double (*)[5][5].

    usuario529758

    20 de octubre de 2013 a las 8:26

  • Si está utilizando plantillas, hacer ambas dimensiones como argumentos de plantilla es más apropiado y es mejor, ya que se puede evitar por completo el acceso al puntero de bajo nivel.

    – leyendas2k

    24 de marzo de 2014 a las 12:43


  • Esto solo funciona si se conoce el tamaño de la matriz en el momento de la compilación.

    – John Doe

    27 de diciembre de 2017 a las 16:51

  • @Georg Code anterior en respuesta es exactamente lo que sugerí. Funciona en GCC 6.3 – demostración en línea. ¿Olvidaste hacer del parámetro una referencia?

    – leyendas2k

    29 de noviembre de 2018 a las 5:39

Sorprendido de que nadie haya mencionado esto todavía, pero simplemente puede crear una plantilla en cualquier cosa compatible con 2D [][] 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 “tipo matriz”, como std::vector<std::vector<T>>o un tipo definido por el usuario para maximizar la reutilización del código.

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...
}

Luego, 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. Sin embargo, podría 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, definiría la plantilla así:

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

  • size_t es el mejor tipo para índices de matriz que int.

    – Andrés Tomazos

    10 de julio de 2013 a las 11:42

1647643272 612 Pasar una matriz 2D a una funcion C
Serguéi Kalinichenko

anArray[10][10] no es un puntero a un puntero, es una porción contigua de memoria adecuada para almacenar 100 valores de tipo double, que el compilador sabe cómo abordar porque usted especificó las dimensiones. Debe 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.

  • size_t es el mejor tipo para índices de matriz que int.

    – Andrés Tomazos

    10 de julio de 2013 a las 11:42

1647643272 949 Pasar una matriz 2D a una funcion C
Mahesh

La matriz unidimensional decae en un puntero apuntando 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]);

Yo preferiría std::vector sobre matrices sin procesar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad