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 un error.
No quiero copiar la matriz cuando la paso. Cualquier cambio realizado 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 también debe aceptar matrices de diferentes tamaños. Entonces, por ejemplo, [10][10]
y [5][5]
. ¿Cómo puedo hacer esto?
c++
arrays
pointers
multidimensional-array
RogerDarwin
fuente
fuente
func(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);
Respuestas:
Hay tres formas de pasar una matriz 2D a una función:
El parámetro es una matriz 2D.
El parámetro es una matriz que contiene punteros
El parámetro es un puntero a un puntero.
fuente
array
conarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
.Tamaño fijo
1. Pase por referencia
En C ++, pasar la matriz por referencia sin perder la información de la dimensión es probablemente la más segura, ya que uno no tiene que preocuparse de que la persona que llama pase una dimensión incorrecta (los indicadores del compilador no coinciden). Sin embargo, esto no es posible con arreglos dinámicos (de libre acceso); funciona para automático ( generalmente apilamiento arreglos ), es decir, la dimensionalidad debe conocerse en tiempo de compilación.
2. Pase por puntero
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. Al igual que (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
process_2d_array_pointer(&a)
la dirección de la matriz y no la dirección del primer elemento por descomposiciónprocess_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 dimensión (es). Estos son más flexibles que los anteriores ya que se pueden pasar invariablemente a ellos conjuntos de diferentes longitudes.
Debe recordarse que no hay tal cosa como 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 a la matriz misma. Pasar siempre 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 descomposición en un puntero .
3. Pase por (valor) un puntero al tipo decaído
Aunque
int array[][10]
está permitido, no lo recomendaría sobre la sintaxis anterior, ya que la sintaxis anterior deja en claro que el identificadorarray
es un puntero único a una matriz de 10 enteros, mientras que esta sintaxis parece que es 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 cierta seguridad ya que el compilador 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 puede omitirse. Vea aquí la justificación de por qué solo se permite omitir la primera dimensión.4. Pase por puntero a puntero
Nuevamente, hay una sintaxis alternativa
int *array[10]
que es la misma queint **array
. En esta sintaxis,[10]
se ignora, ya que se desintegra en un puntero y se convierteint **array
. Quizás sea solo una señal para la persona que llama que la matriz pasada debe tener al menos 10 columnas, incluso cuando se requiere el recuento de filas. En cualquier caso, el compilador no marca ninguna infracción de longitud / tamaño (solo verifica si el tipo pasado es un puntero a puntero), por lo tanto, requiere recuentos de filas y columnas 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 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);
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 a ser pasada; este sustituto se pasa a la función (ver más abajo); todo esto para realizar el mismo trabajo que los métodos anteriores, que son más seguros, limpios y quizás más rápidos.Aquí hay un programa de controlador para probar las funciones anteriores:
fuente
b[i] = a[i];
de, por ejemplo,b[i] = new int[10];
. También se puedeb
asignar dinámicamenteint **b = int *[5];
y seguirá funcionando como está.array[i][j]
en la función en 4) ? Debido a que ha recibido ptr a ptr y no conoce el valor de la última dimensión, ¿qué es necesario para realizar un cambio para el direccionamiento correcto?array[i][j]
es solo aritmética de puntero, es decir, al valor del punteroarray
, agregaríai
y desreferenciaría el resultado comoint*
, a lo que agregaríaj
y desreferenciaría esa ubicación, leyendo unint
. 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 era 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.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):
Las declaraciones de impresión están ahí para mostrar que las matrices se pasan por referencia (al mostrar las direcciones de las variables)
fuente
%p
para imprimir un puntero, e incluso entonces, debe lanzarlovoid *
, de lo contrario,printf()
invoca un comportamiento indefinido. Además, no debe usar el&
operador addressof ( ) al llamar a las funciones, ya que las funciones esperan un argumento de tipodouble (*)[size_y]
, mientras que actualmente las pasadouble (*)[10][10]
ydouble (*)[5][5]
.Sorprende que nadie haya mencionado esto todavía, pero puede simplemente crear plantillas en cualquier cosa que admita semántica en 2D [] [].
Funciona con cualquier estructura de datos 2D "tipo matriz", como
std::vector<std::vector<T>>
un tipo definido por el usuario para maximizar la reutilización del código.fuente
Puede crear una plantilla de función como esta:
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 diferentes tamaños de matriz, esto puede ser costoso. Sin embargo, podría usarlo como envoltorio sobre una función como esta:
Trata la matriz como unidimensional y utiliza la aritmética para calcular los desplazamientos de los índices. En este caso, definiría la plantilla de esta manera:
fuente
size_t
es el mejor tipo para los índices de matriz queint
.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 direccionar porque 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: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.fuente
La matriz unidimensional decae a un puntero que apunta al primer elemento de la matriz. Mientras que una matriz 2D decae a un puntero que apunta a la primera fila. Entonces, el prototipo de la función debería ser:
Preferiría
std::vector
sobre las matrices en bruto.fuente
Puedes hacer algo como esto ...
Su salida será la siguiente ...
fuente
Aquí hay un ejemplo de matriz de vectores de vectores
salida:
fuente
Podemos usar varias formas de pasar una matriz 2D a una función:
Usando un puntero único , tenemos que encasillar la matriz 2D.
Usando doble puntero De esta manera, también escribimos la matriz 2D
fuente
Una cosa importante para pasar matrices multidimensionales es:
First array dimension
No es necesario especificarlo.Second(any any further)dimension
debe especificarse1.Cuando solo la segunda dimensión está disponible globalmente (ya sea como macro o como constante global)
2.Utilizando un puntero único : en este método, debemos escribir la matriz 2D al pasar a la función.
fuente
Puede usar la facilidad de plantilla en C ++ para hacer esto. Hice algo como esto:
El problema con este enfoque es que por cada valor de col que proporcione, se crea una nueva definición de función utilizando la plantilla. entonces,
crea una instancia de la plantilla dos veces para producir 2 definiciones de funciones (una donde col = 3 y otra donde col = 5).
fuente
Si desea pasar
int a[2][3]
avoid func(int** pp)
lo que necesita medidas auxiliares de la siguiente manera.Como el primero
[2]
puede especificarse implícitamente, puede simplificarse aún más como.fuente
En el caso de que desee pasar una matriz 2D de tamaño dinámico a una función, utilizar algunos punteros podría funcionar para usted.
fuente
Se le permite omitir la dimensión más a la izquierda, por lo que termina con dos opciones:
Esto es lo mismo con los punteros:
El estándar C ++ permite la descomposición de una matriz de N dimensiones en un puntero a una matriz de dimensiones N-1 , ya que puede perder la dimensión más a la izquierda y aún así poder acceder correctamente a los elementos de la matriz con información de dimensiones N-1.
Detalles aquí
Sin embargo, las matrices y los punteros no son lo mismo : una matriz puede decaer en un puntero, pero un puntero no tiene un estado sobre el tamaño / configuración de los datos a los que apunta.
A
char **
es un puntero a un bloque de memoria que contiene punteros de caracteres , que a su vez apuntan a bloques de memoria de caracteres. Achar [][]
es un bloque de memoria único que contiene caracteres. Esto tiene un impacto en cómo el compilador traduce el código y cómo será el rendimiento final.Fuente
fuente