C, которая принимает const 2d array

Я хочу написать функцию C, которая принимает динамический 2D-массив в качестве ввода, но не изменяет массив.

Я стараюсь быть постоянным, не только для того, чтобы сделать мой код более ясным, а потому, что мои функции будут вызываться из кода C ++, а C ++ довольно остерегается этих вещей.

Как объявить функцию, чтобы взять указатель «const» указателю, т. Е. Как указать, что функция не изменит содержимое массива 2d?

Далее следует конкретный, сверхпростой пример. Я использую двумерный массив двойников, т. Е. Double **, чтобы представить квадратную матрицу в C размера nxn, и я хочу написать функцию, которая вычисляет след одного из этих матриц:

#include  #include  double **sqr_matrix_new(int n) { double **a = calloc(n, sizeof(double*)); int i; for (i=0; i < n; ++i) a[i] = calloc(n, sizeof(double)); return a; } void sqr_matrix_free(double **a, int n) { int i; for (i=0; i < n; ++i) free(a[i]); free(a); } double sqr_matrix_trace(double **a, int n) { double trace; int i; for (i=0, trace=0.0; i < n; ++i) trace += a[i][i]; return trace; } double sqr_matrix_trace_const(const double * const *a, int n) { double trace; int i; for (i=0, trace=0.0; i < n; ++i) trace += a[i][i]; return trace; } int main(int argc, char *argv[]) { int n = 10; double **a = sqr_matrix_new(n); int i, j, k; for (i=0, k=0; i < n; ++i){ for (j=0; j < n; ++j) a[i][j] = k++; } printf("trace is %g\n", sqr_matrix_trace(a, n)); printf("trace is %g\n", sqr_matrix_trace_const(a, n)); printf("trace is %g\n", sqr_matrix_trace_const((const double * const *)a, n)); sqr_matrix_free(a, n); } 

В приведенном выше примере обе версии функции трассировки sqr_matrix_trace () и sqr_matrix_trace_const () компилируются чисто (последняя является той, которую я предпочитаю, потому что она наглядно демонстрирует, что изменения матрицы не будут изменены), но вызов

 sqr_matrix_trace_const(a, n) 

выдает следующее предупреждение:

 sqr_matrix.c: In function 'main': sqr_matrix.c:44: warning: passing argument 1 of 'sqr_matrix_trace_const' from incompatible pointer type sqr_matrix.c:27: note: expected 'const double * const*' but argument is of type 'double **' 

Бросок преодолевает это:

 sqr_matrix_trace_const((const double * const *)a, n) 

но неправильно использовать приведение, чтобы использовать для преодоления неудобств компилятора.

В качестве альтернативы я мог бы подавить предупреждение компилятора, но это ошибка.

Итак, я хочу, чтобы мой код был скомпилирован, и я хочу передать константу динамического 2D-массива, переданного функции, не прибегая к кастомизации. Это похоже на законную цель. Это возможно? Если нет, какова стандартная / принятая практика для этого?

Правила продвижения C const не позволяют продвигать с T ** на const T const * . В соответствии с 6.5.16.1 1 (что относится к вызовам функций, а также к назначениям на 6.5.2.2 2), преобразование указателей может только добавлять квалификаторы к указанному типу.

Это необходимо для предотвращения использования кода (пример из 6.5.16.1 6):

 const char **cpp; char *p; const char c = 'A'; cpp = &p; // constraint violation *cpp = &c; // valid *p = 0; // valid 

Правильно заметить, что const *const *cpp = &p является безопасным, потому что предотвращается *cpp = &c , но это довольно неясный случай, когда он не рассматривается в стандарте.

Вывод: вы можете и должны указывать на const double *const * самостоятельно.

Обратите внимание, что было бы более эффективно использовать один массив типа double * с длиной n * n и самостоятельно индексировать нужный массив: d[i][j] становится d[i * n + j] .

Этот компилятор C ++ допустил бы это.

Что касается C, то квалифицированные типы указателей не применяются рекурсивно .

Если ваши матричные данные действительно двумерные и прямоугольные (без «оборванного правого края»), я не понимаю, почему вы не представляете его как один double * для первого элемента вместе с целыми числами, задающими ширину и высоту. Это позволит вам сократить количество ассигнований, необходимых для инициализации матрицы, но также сделать ее представимой как простой старый const double * .