C, which takes a const 2d array

I want to write a C function that accepts a dynamic 2D array as input, but does not modify the array.

I try to be correct, but not only to make my code more clear, but because my functions will be called from C ++ code, and C ++ is quite wary of these things.

How to declare a function to take a pointer "const" to a pointer, i.e. how to indicate that the function will not change the contents of the 2d array?

The following is a concrete, super-simple example. I am using a 2D array of twins, i.e. Double ** to represent a square matrix in C of size nxn, and I want to write a function that calculates the trace of one of these matrices:

#include <stdlib.h> #include <stdio.h> 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); } 

In the above example, both versions of the sqr_matrix_trace () and sqr_matrix_trace_const () trace functions compile cleanly (the latter is the one I prefer, because it clearly demonstrates that there will be no changes in the given matrix), but the call

 sqr_matrix_trace_const(a, n) 

issues the following warning:

 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 **' 

Listing overcomes this:

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

but it's wrong to use a cast to use to overcome the inconvenience of the compiler.

Alternatively, I could suppress a compiler warning, but this is an error message.

So, I want my code to be compiled, and I want to pass the constant of the dynamic 2D array passed to the function without resorting to customization. This seems like a legitimate aim. Is it possible? If not, what is the standard / accepted practice for this?

+4
source share
3 answers

The promotion rules C const do not allow promotion from T ** to const T const * . According to 6.5.16.1 1 (which applies to function calls, as well as assignments according to 6.5.2.2 2), pointer conversion can only add qualifiers to the specified type.

This is necessary to prevent the use of code (example from 6.5.16.1 6):

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

It is correct to note that const *const *cpp = &p is safe because *cpp = &c prevented, but this is a rather obscure case that it does not cover in the standard.

Conclusion: you can and should use const double *const * yourself.

Note that it would be more efficient to use one double * array with length n * n and do all the necessary indexing of the array yourself: d[i][j] becomes d[i * n + j] .

+3
source

The C ++ compiler will allow this.

For C, qualified pointer types are not applied recursively .

+1
source

If your matrix data is really two-dimensional and rectangular (without a “torn right edge”), I don’t understand why you can’t imagine it as the only double * for the first element along with integers giving the width and height. This will allow you to reduce the number of allocations needed to initialize the matrix, but also make it representable as a plain old const double * .

0
source

All Articles