programas - manual completo de c++ pdf
Pasando una matriz 2D a una funciĆ³n de C++ (12)
Tamaño fijo
1. Pase 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 la más segura, ya que no hay que preocuparse de que la persona que llama pase una dimensión incorrecta (indicadores del compilador cuando no coinciden). Sin embargo, esto no es posible con arreglos dinámicos (freestore); funciona solo para arreglos automáticos ( generalmente de pila ), es decir, la dimensionalidad debe ser conocida 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 el paso por el tipo de puntero decaído de la matriz (3) , que es el método común y popular, aunque es menos seguro que este pero más flexible. Al igual que en 1 , use 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 se debe pasar process_2d_array_pointer(&a)
y no la dirección del primer elemento mediante 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, lo que garantiza que la persona que llama pasa las dimensiones requeridas. La función solo se basa en lo que la persona que llama pasa como dimensión (s). Estos son más flexibles que los anteriores, ya que invariablemente se les pueden pasar matrices 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 una referencia (1) ]; (2) pasa un puntero a la matriz y no la matriz en sí. Pasar siempre una matriz tal como es 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
// 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 se permite int array[][10]
, no lo recomendaría sobre la sintaxis anterior ya que la sintaxis anterior deja claro que la array
identificadores 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 columna, 10 aquí), pero el número de filas es desconocido y, por lo tanto, se pasa como un argumento. En este caso, existe cierta 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. Consulte aquí la razón por la cual 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;
}
}
De nuevo, hay una sintaxis alternativa de int *array[10]
que es la misma que int **array
. En esta sintaxis, el [10]
se ignora, ya que se descompone en un puntero y se convierte en una 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 si se requiere el conteo de filas. En cualquier caso, el compilador no marca ninguna violación de longitud / tamaño (solo verifica si el tipo que se pasa es un puntero a otro), por lo que es necesario que los recuentos de filas y columnas como parámetro tengan sentido aquí.
Nota: (4) es la opción menos segura, ya que casi no tiene ningún tipo de verificación y el 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 potencialmente 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 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, 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**
}
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 en 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
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 también matrices de diferentes tamaños. Así, por ejemplo, [10][10]
y [5][5]
. ¿Cómo puedo hacer esto?
Aquí hay un vector de ejemplo de matriz de vectores.
#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
Hay tres formas de pasar una matriz 2D a una función:
El parámetro es una matriz 2D.
int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array);
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);
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);
La matriz unidimensional decae en un puntero puntero que apunta al primer elemento de la matriz. Mientras que una matriz 2D decae en un puntero que apunta a la primera fila. Entonces, la función prototipo debería ser -
void myFunction(double (*myArray) [10]);
Preferiría std::vector
sobre matrices en bruto.
Podemos usar varias formas de pasar una matriz 2D a una función:
Usando un solo puntero tenemos que encasillar la matriz 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 doble puntero De esta manera, también encasillamos la matriz 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;
}
Puede usar la facilidad de plantilla 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, se crea una instancia de la definición de una nueva función utilizando la plantilla. asi que,
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).
Puedes 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. Podrías usarlo como una envoltura sobre una función como esta aunque:
void myFunction(double * arr, int R, int C)
{
arr[x * C + y] = 5;
etc...
}
Trata la matriz como unidimensional, y usa la aritmética para averiguar las compensaciones de los índices. En este caso, definirías la plantilla así:
template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
myFunction(*myArray, R, C);
}
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
Sorprendido de que nadie haya mencionado esto todavía, puede simplemente crear una plantilla en cualquier cosa que admita la 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.
Una cosa importante para pasar arrays multidimensionales es:
-
First array dimension
no necesita ser especificada. -
Second(any any further)dimension
debe especificar laSecond(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.Utilizando un único puntero : En este método, debemos hacer una conversión a máquina de 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;
}`
Una modificación de 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)
anArray[10][10]
no es un puntero a un puntero, es un trozo contiguo de memoria adecuado para almacenar 100 valores de tipo doble, que el compilador sabe cómo abordar porque especificó las dimensiones. Necesitas 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 a excepción de diez.
La mejor solución en C ++ es usar std::vector<std::vector<double> >
: es casi tan eficiente y significativamente más conveniente.