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 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
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. 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ón 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 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
// 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 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
// 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 int *array[10]
que es la misma que int **array
. En esta sintaxis, [10]
se ignora, ya que se desintegra en un puntero y se convierte int **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:
#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**
}