Uso de matriz estándar en C con decaimiento de tipo natural de matriz a ptr
@Bo Persson afirma correctamente en su gran respuesta aquí :
Al pasar una matriz como parámetro, esto
void arraytest(int a[])
significa exactamente lo mismo que
void arraytest(int *a)
Sin embargo, permítame agregar también que las dos formas anteriores también:
significa exactamente lo mismo que
void arraytest(int a[0])
que significa exactamente lo mismo que
void arraytest(int a[1])
que significa exactamente lo mismo que
void arraytest(int a[2])
que significa exactamente lo mismo que
void arraytest(int a[1000])
etc.
En cada uno de los ejemplos de arreglos anteriores, el tipo de parámetro de entrada decae a anint *
, y se puede llamar sin advertencias ni errores, incluso con las opciones de compilación -Wall -Wextra -Werror
activadas (consulte mi repositorio aquí para obtener detalles sobre estas 3 opciones de compilación), como esta:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
Como cuestión de hecho, el valor "tamaño" ( [0]
, [1]
, [2]
, [1000]
, etc.) dentro del parámetro de matriz aquí es aparentemente sólo por propósitos / estético auto-documentación, y puede ser cualquier número entero positivo (size_t
tipo I creo) que usted quiera!
En la práctica, sin embargo, debe usarlo para especificar el tamaño mínimo de la matriz que espera que reciba la función, de modo que al escribir código sea fácil de rastrear y verificar. El estándar MISRA-C-2012 ( compre / descargue el PDF de la versión 2012 de 236 páginas del estándar por £ 15.00 aquí ) va tan lejos como para afirmar (énfasis agregado):
Regla 17.5 El argumento de la función correspondiente a un parámetro declarado que tiene un tipo de matriz deberá tener un número apropiado de elementos.
...
Si un parámetro se declara como una matriz con un tamaño específico, el argumento correspondiente en cada llamada de función debe apuntar a un objeto que tenga al menos tantos elementos como la matriz.
...
El uso de un declarador de matriz para un parámetro de función especifica la interfaz de la función con más claridad que el uso de un puntero. El número mínimo de elementos esperado por la función se indica explícitamente, mientras que esto no es posible con un puntero.
En otras palabras, recomiendan usar el formato de tamaño explícito, aunque el estándar C técnicamente no lo hace cumplir, al menos ayuda a aclararle a usted como desarrollador y a otros que usan el código qué tamaño de matriz espera la función. que pases.
Forzar la seguridad de tipos en matrices en C
Como señala @Winger Sendon en un comentario debajo de mi respuesta, podemos obligar a C a tratar un tipo de matriz para que sea diferente según el tamaño de la matriz !
Primero, debe reconocer que en mi ejemplo anterior, el uso de int array1[2];
este tipo de: arraytest(array1);
hace array1
que se descomponga automáticamente en un int *
. SIN EMBARGO, si toma la dirección de en su array1
lugar y llama arraytest(&array1)
, ¡obtendrá un comportamiento completamente diferente! ¡Ahora, NO se descompone en un int *
! En cambio, el tipo de &array1
es int (*)[2]
, que significa "puntero a una matriz de tamaño 2 de int" , o "puntero a una matriz de tamaño 2 de tipo int" . Por lo tanto, puede FORZAR a C para verificar la seguridad de tipos en una matriz, como esta:
void arraytest(int (*a)[2])
{
}
Esta sintaxis es difícil de leer, pero similar a la de un puntero de función . La herramienta en línea, cdecl , nos dice que eso int (*a)[2]
significa: "declarar como puntero a la matriz 2 de int" (puntero a la matriz de 2 int
s). NO confunda esto con la versión sin paréntesis:, int * a[2]
que significa: "declare a como matriz 2 de puntero a int" (matriz de 2 punteros a int
).
Ahora, esta función REQUIERE que la llames con el operador de dirección ( &
) así, usando como parámetro de entrada un PUNTERO A UN ARREGLO DEL TAMAÑO CORRECTO !:
int array1[2];
arraytest(&array1);
Esto, sin embargo, producirá una advertencia:
int array1[2];
arraytest(array1);
Puede probar este código aquí .
Para forzar al compilador de C a convertir esta advertencia en un error, de modo que siempre DEBE llamar arraytest(&array1);
usando solo una matriz de entrada del tamaño y tipo correctos ( int array1[2];
en este caso), agregue -Werror
a sus opciones de compilación. Si está ejecutando el código de prueba anterior en onlinegdb.com, haga clic en el icono de engranaje en la parte superior derecha y haga clic en "Banderas extra del compilador" para escribir esta opción. Ahora, esta advertencia:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
se convertirá en este error de compilación:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
Tenga en cuenta que también puede crear punteros de "tipo seguro" para matrices de un tamaño determinado, como este:
int array[2];
int (*array_p)[2] = &array;
... pero no lo recomiendo necesariamente , ya que me recuerda muchas de las payasadas de C ++ que se usan para forzar la seguridad de los tipos en todas partes, con el costo excepcionalmente alto de la complejidad de la sintaxis del lenguaje, la verbosidad y la dificultad para diseñar el código, y que no me gusta y he despotricado muchas veces antes (por ejemplo, consulte "Mis pensamientos sobre C ++" aquí ).
Para pruebas y experimentación adicionales, consulte también el enlace que se encuentra a continuación.
Referencias
Vea los enlaces de arriba. También:
- Mi experimentación de código en línea: https://onlinegdb.com/B1RsrBDFD