¿Qué define la siguiente firma de función en C?
#include <stdio.h> #include <string.h> void f(int a[const volatile static 2]) { (void)a; } int main() { int b[1]; f(b); }https://godbolt.org/z/6qPxaM1vM
No entiendo el significado de const / volatile / static en este lugar, pero parece compilarse, así que supongo que tiene un significado.
Gracias
Un parámetro de tipo matriz se ajusta automáticamente a un tipo de puntero. Por lo tanto, un parámetro declarado como:
int A[3]se transforma en:
int *A Sin embargo, con la notación de matriz no hay un lugar intuitivo para agregar el calificador de la variable a sí misma (no los datos señalados por a ). Por lo tanto, el estándar C permite poner esos especificadores entre paréntesis.
Por lo tanto:
void f(int a[const volatile restrict 2])es en realidad:
void (int * const volatile restrict a) El tamaño ( 2 en el ejemplo anterior) generalmente se ignora. La excepción es cuando se usa una palabra clave static . Proporciona una pista para un compilador de que al menos los elementos en las direcciones a + 0 a a + size - 1 son válidos. Esta sugerencia en teoría debería mejorar la optimización al simplificar la vectorización. Sin embargo, AFAIK es ignorado por los principales compiladores.
Tu codigo:
int b[1]; f(b); está activando UB porque solo el elemento en la dirección b + 0 es válido, mientras que la sugerencia requiere que b+0 y b+1 sean válidos. Los mejores compiladores/desinfectadores deberían detectar eso.
La static también es útil para la autodocumentación y la detección de errores, como decir que al menos n elementos señalados por un puntero deben ser válidos:
void fun(int n, int arr[static n])o incluso decir que el puntero nunca es NULL :
void fun(int ptr[static 1]); Además, la sintaxis int buf[static n] es una buena pista visual de que algo en realidad no es una matriz. Ayuda a evitar un error común al intentar adquirir el tamaño de una "matriz" con la sintaxis sizeof buf .
EDITAR
Como se indica en el comentario, la palabra "sugerencia" puede ser un poco engañosa porque podría interpretarse que la violación de la "sugerencia" no es un error, aunque puede resultar en algo no óptimo (como la degradación del rendimiento). En realidad, es más bien un requisito, cuya violación da como resultado un comportamiento indefinido.
Esta es una característica levemente útil introducida en C99. Desde C17 6.7.6.3/7:
La declaración de un parámetro como "matriz de tipo" se ajustará a "puntero calificado a tipo", donde los calificadores de tipo (si los hay) son los especificados entre [ y ] de la derivación del tipo de matriz. Si la palabra clave
statictambién aparece dentro de [ y ] de la derivación del tipo de matriz, entonces, para cada llamada a la función, el valor del argumento real correspondiente proporcionará acceso al primer elemento de una matriz con al menos tantos elementos como se especifica por la expresión de tamaño.
Es decir, en este caso, los calificadores const y volatile significan que la matriz se descompone en un puntero de tipo int* const volatile a . Dado que este tipo calificado es local para la función, significa muy poco para la persona que llama, ya que un puntero pasado se asigna mediante la conversión de lvalue y aún puede ser un tipo de puntero no calificado.
El static es un poco más útil, ya que supuestamente permite que el compilador verifique el tamaño en tiempo de compilación, aunque en la práctica parece que los compiladores principales (actualmente) solo logran verificar que el puntero no sea nulo. Por ejemplo f(0) en clang da:
advertencia: nulo pasado a un destinatario que requiere un argumento no nulo [-Wnonnull]
Curiosamente, f(0) en gcc 11.1 y posteriores dice:
advertencia: el argumento 1 a 'int[static 8]' es nulo donde no se esperaba [-Wnonnull]"
No tengo idea de dónde proviene el 8 , supongo que es un error tipográfico/menor del compilador (el puntero deteriorado tiene un tamaño de 8 bytes).