Несовместимые типы указателей и константы

У меня есть функция, принимающая статический двумерный массив и рассматривающий элементы элементов массива как постоянные:

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

Я пытаюсь вызвать такую ​​функцию следующим образом:

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

При компиляции с gcc (без какого-либо флага) я получаю следующее предупреждение:

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]);

Если я удалю прототип const из test_function, предупреждение исчезнет. Но это не совсем то, что я хочу.

При компиляции с clang как с -pedantic-errors, так и -Wall я не получаю предупреждения о несовместимости указателя.

Я просто хотел бы понять, почему gcc выводит такое предупреждение в этом случае. Почему мои указатели/массивы несовместимы?

Ответ 1

GCC имеет право на букву стандарта, а Clang ошибочен.

6.3.2.3/2:

Для любого определителя q указатель на не-q-квалифицированный тип может быть преобразован в указатель на q-квалифицированную версию типа;

Выглядит очень многообещающе. Но держитесь.

6.2.5/26:

Производный тип не определяется квалификаторами (если они есть) того типа, из которого он получен

Это положение стандарта, применяемого специально для массивов, не является необходимым и может быть легко отменено. То есть const char[3] может быть легко сконструирована с использованием const-версии версии char[3]. Но это не так. Это просто разные, несовместимые типы. На самом деле в C нет никаких типов конструкций с независимым от const-типа, поэтому вы не можете иметь версию char[3] с поддержкой const. Это стандарт, который мы имеем и должен жить.

Ответ 2

Из C-FAQ [Вопрос 11.10]

В C, если вы должны назначить или передать указатели, которые имеют квалификатор несоответствие, отличное от первого уровня косвенности, вы должны использовать явные приведения (например, (const char **) в этом случае), хотя как всегда, необходимость в таком акте может указывать на более глубокую проблему, которая литье действительно не исправляется.

В вашем случае:

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