Lo que dices en tu publicación es absolutamente correcto. Yo diría que todos los desarrolladores de C llegan exactamente al mismo descubrimiento y a la misma conclusión cuando (si) alcanzan cierto nivel de competencia con el lenguaje C.
Cuando las características específicas de su área de aplicación requieren una matriz de tamaño fijo específico (el tamaño de la matriz es una constante en tiempo de compilación), la única forma correcta de pasar dicha matriz a una función es mediante el uso de un parámetro de puntero a matriz
void foo(char (*p)[10]);
(en lenguaje C ++ esto también se hace con referencias
void foo(char (&p)[10]);
).
Esto permitirá la verificación de tipos a nivel de idioma, lo que garantizará que la matriz del tamaño exactamente correcto se proporcione como argumento. De hecho, en muchos casos la gente usa esta técnica implícitamente, sin siquiera darse cuenta, ocultando el tipo de matriz detrás de un nombre typedef
typedef int Vector3d[3];
void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);
Tenga en cuenta además que el código anterior es invariante con relación a que el Vector3d
tipo sea una matriz o un struct
. Puede cambiar la definición de Vector3d
en cualquier momento de una matriz a ay struct
volver, y no tendrá que cambiar la declaración de la función. En cualquier caso, las funciones recibirán un objeto agregado "por referencia" (hay excepciones a esto, pero dentro del contexto de esta discusión esto es cierto).
Sin embargo, no verá que este método de transferencia de matrices se use explícitamente con demasiada frecuencia, simplemente porque demasiadas personas se confunden con una sintaxis bastante complicada y simplemente no se sienten lo suficientemente cómodas con tales características del lenguaje C para usarlas correctamente. Por esta razón, en la vida real promedio, pasar una matriz como puntero a su primer elemento es un enfoque más popular. Simplemente parece "más simple".
Pero en realidad, usar el puntero al primer elemento para pasar la matriz es una técnica muy especializada, un truco, que tiene un propósito muy específico: su único propósito es facilitar el paso de matrices de diferentes tamaños (es decir, tamaño en tiempo de ejecución) . Si realmente necesita poder procesar matrices de tamaño en tiempo de ejecución, entonces la forma correcta de pasar dicha matriz es mediante un puntero a su primer elemento con el tamaño concreto proporcionado por un parámetro adicional
void foo(char p[], unsigned plen);
De hecho, en muchos casos es muy útil poder procesar matrices de tamaño de tiempo de ejecución, lo que también contribuye a la popularidad del método. Muchos desarrolladores de C simplemente nunca encuentran (o nunca reconocen) la necesidad de procesar una matriz de tamaño fijo, por lo que permanecen ajenos a la técnica adecuada de tamaño fijo.
Sin embargo, si el tamaño de la matriz es fijo, pasarlo como puntero a un elemento
void foo(char p[])
es un error importante a nivel de técnica, que lamentablemente está bastante extendido en estos días. Una técnica de puntero a matriz es un enfoque mucho mejor en tales casos.
Otra razón que podría obstaculizar la adopción de la técnica de paso de arreglos de tamaño fijo es el predominio del enfoque ingenuo para la tipificación de arreglos asignados dinámicamente. Por ejemplo, si el programa requiere arreglos fijos de tipo char[10]
(como en su ejemplo), un desarrollador promedio usará malloc
arreglos como
char *p = malloc(10 * sizeof *p);
Esta matriz no se puede pasar a una función declarada como
void foo(char (*p)[10]);
lo que confunde al desarrollador promedio y les hace abandonar la declaración de parámetros de tamaño fijo sin pensarlo más. Sin embargo, en realidad, la raíz del problema radica en el malloc
enfoque ingenuo . El malloc
formato que se muestra arriba debe reservarse para matrices de tamaño en tiempo de ejecución. Si el tipo de matriz tiene un tamaño en tiempo de compilación, una mejor manera de malloc
hacerlo sería la siguiente
char (*p)[10] = malloc(sizeof *p);
Esto, por supuesto, se puede pasar fácilmente a lo declarado anteriormente foo
foo(p);
y el compilador realizará la verificación de tipo adecuada. Pero, de nuevo, esto es demasiado confuso para un desarrollador de C no preparado, por lo que no lo verá con demasiada frecuencia en el código "típico" promedio de todos los días.
10
puede ser reemplazada por cualquier variable en el alcance