Tipos de punteros incompatibles y constness

Tengo una función que toma una matriz bidimensional estática y trata los elementos de la matriz como constantes:

void test_function(const char arr[3][3]); 

Estoy tratando de llamar a una función como la siguiente:

 char my_var[3][3] = { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }; test_function(my_var); 

Al comstackr con gcc (sin ninguna marca), recibo la siguiente advertencia:

 test.c:9:8: warning: passing argument 1 of 'test_function' from incompatible pointer type test_function(my_var); ^ test.c:4:6: note: expected 'const char (*)[3]' but argument is of type 'char (*)[3]' void test_function(const char arr[3][3]); 

Si quito la const del test_function de test_function , la advertencia desaparece. Pero no es realmente lo que quiero.

Al comstackr con clang con ambos -pedantic-errors -Wall y -Wall no recibo ninguna advertencia sobre la incompatibilidad de punteros.

Solo me gustaría entender por qué gcc produce una advertencia de este tipo en este caso. ¿Por qué mis punteros / arrays serían incompatibles?

GCC tiene razón con la letra del estándar y Clang está equivocado.

6.3.2.3/2:

Para cualquier calificador q , un puntero a un tipo no calificado por q puede convertirse en un puntero a la versión calificada por el q ;

Parece muy prometedor. Pero espera.

6.2.5 / 26:

Un tipo derivado no está calificado por los calificadores (si los hay) del tipo del que se deriva

Esta disposición de la norma, tal como se aplica específicamente a las matrices, no es necesaria y podría revertirse fácilmente. Es decir, const char[3] podría convertirse fácilmente en una versión de char[3] calificada por const. Pero no lo es. Solo son tipos diferentes, incompatibles. De hecho, en C no hay tipos de arreglos constantes, por lo que no puede tener una versión cualificada para caracteres const char[3] . Esa es la norma que tenemos y con la que debemos vivir.

De la C-FAQ [ Pregunta 11.10 ]

En C, si debe asignar o pasar punteros que tengan discrepancias en el calificador en un nivel distinto del primer direccionamiento indirecto, debe usar conversiones explícitas (por ejemplo, (const char **) en este caso), aunque como siempre, la necesidad de tal El reparto puede indicar un problema más profundo que el reparto realmente no resuelve.

En tu caso:

 test_function((const char (*)[3])my_var);