significado constant c const-correctness

constant - ¿Corrección de const para punteros de matriz?



const php (3)

Alguien hizo un argumento diciendo que en la C moderna, siempre deberíamos pasar matrices a funciones a través de un puntero de matriz, ya que los punteros de matriz tienen una tipificación fuerte. Ejemplo:

void func (size_t n, int (*arr)[n]); ... int array [3]; func(3, &array);

Esto parecía que podría ser una buena idea para evitar todo tipo de errores relacionados con el tipo y la matriz fuera de los límites. Pero entonces se me ocurrió que no sé cómo aplicar la corrección constante a esto.

Si hago void func (size_t n, const int (*arr)[n]) entonces const es correcto. Pero luego ya no puedo pasar la matriz, debido a tipos de punteros incompatibles. int (*)[3] versus const int (*)[3] . El calificador pertenece a los datos apuntados y no al puntero en sí.

Un reparto explícito en la persona que llama arruinaría toda la idea de una mayor seguridad de tipos.

¿Cómo aplico la corrección constante a los punteros de matriz pasados ​​como parámetros? ¿Es posible?

EDITAR

Al igual que la información, alguien dijo que la idea de pasar matrices por punteros como este probablemente se origina en MISRA C ++: 2008 5-2-12. Ver por ejemplo el estándar C ++ de alta integridad de PRQA .


El estándar C dice que (sección: §6.7.3 / 9):

Si la especificación de un tipo de matriz incluye cualquier calificador de tipo, el tipo de elemento está calificado, no el tipo de matriz . [...]

Por lo tanto, en el caso de const int (*arr)[n] , const se aplica a los elementos de la matriz en lugar de la propia matriz arr . arr es de tipo puntero a matriz [n] de const int mientras está pasando un parámetro de tipo puntero a matriz [n] de int . Ambos tipos son incompatibles.

¿Cómo aplico la corrección constante a los punteros de matriz pasados ​​como parámetros? ¿Es posible?

No es posible. No hay manera de hacer esto en el estándar C sin usar la conversión explícita.

Pero, GCC permite esto como una extension :

En GNU C, los punteros a matrices con calificadores funcionan de manera similar a los punteros a otros tipos calificados. Por ejemplo, un valor de tipo int (*)[5] se puede utilizar para inicializar una variable de tipo const int (*)[5] . Estos tipos son incompatibles en ISO C porque el calificador const se adjunta formalmente al tipo de elemento de la matriz y no a la propia matriz .

extern void transpose (int N, int M, double out[M][N], const double in[N][M]); double x[3][2]; double y[2][3]; ... transpose(3, 2, y, x);

Lecturas adicionales: puntero a matriz con calificador const en C y C ++


No hay manera de hacerlo excepto por el elenco. Esto es un inconveniente importante de la idea de pasar matrices de esta manera.

Aquí hay un hilo similar en el que las reglas de C se comparan con las reglas de C ++. De esta comparación podríamos concluir que las reglas de C no están tan bien diseñadas, porque su caso de uso es válido pero C no permite la conversión implícita. Otro ejemplo es la conversión de T ** a T const * const * ; Esto es seguro pero no está permitido por C.

Tenga en cuenta que dado que n no es una expresión constante, int n, int (*arr)[n] no tiene ningún tipo de seguridad añadido en comparación con int n, int *arr . Aún conoce la longitud (n), y todavía es un comportamiento indefinido silencioso para acceder fuera de los límites, y un comportamiento indefinido silencioso para pasar una matriz que no es realmente la longitud n .

Esta técnica tiene más valor en el caso de pasar matrices no VLA, cuando el compilador debe informar si pasa un puntero a una matriz de la longitud incorrecta.


OP describe una función func() que tiene la siguiente firma.

void func(size_t n, const int (*arr)[n])

OP quiere llamarlo pasando varios arreglos.

#define SZ(a) (sizeof(a)/sizeof(a[0])) int array1[3]; func(SZ(array1), &array1); // problem const int array2[3] = {1, 2, 3}; func(SZ(array2), &array2);

¿Cómo aplico la corrección constante a los punteros de matriz pasados ​​como parámetros?

Con C11, use _Generic para hacer el casting según sea necesario. La conversión solo ocurre cuando la entrada es del tipo no const aceptable, manteniendo así la seguridad del tipo. Asi es como se hace. OP puede considerarlo "hinchado", ya que es similar a this . Este enfoque simplifica la llamada de macro / función a solo 1 parámetro.

void func(size_t n, const int (*arr)[n]) { printf("sz:%zu (*arr)[0]:%d/n", n, (*arr)[0]); } #define funcCC(x) func(sizeof(*x)/sizeof((*x)[0]), / _Generic(x, / const int(*)[sizeof(*x)/sizeof((*x)[0])] : x, / int(*)[sizeof(*x)/sizeof((*x)[0])] : (const int(*)[sizeof(*x)/sizeof((*x)[0])])x / )) int main(void) { #define SZ(a) (sizeof(a)/sizeof(a[0])) int array1[3]; array1[0] = 42; // func(SZ(array1), &array1); const int array2[4] = {1, 2, 3, 4}; func(SZ(array2), &array2); // Notice only 1 parameter to the macro/function call funcCC(&array1); funcCC(&array2); return 0; }

Salida

sz:4 (*arr)[0]:1 sz:3 (*arr)[0]:42 sz:4 (*arr)[0]:1

Alternativamente el código podría usar

#define funcCC2(x) func(sizeof(x)/sizeof((x)[0]), / _Generic(&x, / const int(*)[sizeof(x)/sizeof((x)[0])] : &x, / int(*)[sizeof(x)/sizeof((x)[0])] : (const int(*)[sizeof(x)/sizeof((x)[0])])&x / )) funcCC2(array1); funcCC2(array2);