2

Context

I am working on a computational fluid dynamics code where I work with vector and matrices. Consequently I want to pass them to functions every once in a while.

The memory for vectors and matrices is allocated as follows consequently throughout the code

double* vec = calloc(n, sizeof(double));
double* mat = calloc(n * m, sizeof(double));

meaning I would like to access matrices like this mat[i * m + j].

In order to "auto-document" functions I would like to include the size of these arrays into the function prototype. For vectors this works flawlessly and looks something like this

// vecmat.h

void vec_print(size_t n, double const vec[restrict n]);

// vecmat.c

void vec_print(size_t n, double const vec[restrict n])
{
    for (size_t i = 0; i < n; ++i) {
        printf(DBL_FMT, vec[i]);
    }
    putchar('\n');
}

Problem

To keep things consistent, I would like to do the same thing with matrices, so I did this

// vecmat.h

void mat_print(size_t n, size_t m, double const mat[restrict n * m]);

// vecmat.c

void mat_print(size_t n, size_t m, double const mat[restrict n * m])
{
    for (size_t i = 0; i < n; ++i) {
        for (size_t j = 0; j < m; ++j) {
            printf(DBL_FMT, mat[i * m + j]);
        }
        putchar('\n');
    }
    putchar('\n');
}

Here comes the issue though. When I compile, I get the following warning

src/vecmat.c:21:49: warning: argument 3 of type ‘const double[restrict  n * m]’ declared with mismatched bound ‘n * m’ [-Wvla-parameter]
   21 | void mat_print(size_t n, size_t m, double const mat[restrict n * m])
      |                                    ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
In file included from src/vecmat.c:1:
src/vecmat.h:16:49: note: previously declared as ‘const double[restrict  n * m]’ with bound ‘n * m’
   16 | void mat_print(size_t n, size_t m, double const mat[restrict n * m]);
      |                                    ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~

The code compiles fine and also produces the expected output, but I would like to get rid of the warning message. I know that C does not use the provided dimension n * m of the array, but rather silently converts the argument into double* restrict mat. The problem seems to have something to do with the fact that I am using a multiplication and gcc somehow thinks that the array bounds are inconsistent.

Question

Can I somehow modify my syntax to avoid this compiler warning, or can I somehow disable this warning in gcc altogether? (gcc does not have the flag -Wno-vla-parameter)

EDIT -Wno-vla-parameter does exist and removes the warnings

Solution

Matrices should be allocated as follows:

double (*mat)[m] = calloc(n * sizeof(*mat));

The function calls with matrices in them should be altered as follows:

void mat_print(size_t n, size_t m, double const mat[restrict n][m]);

This makes it possible to access the matrix elements in a simpler form mat[i][j].

Unfortunately gcc still complains, because the function uses const. (https://godbolt.org/z/6xh5Exsff) If this also has a clever fix my initial and all subsequent problems would be solved :D

Additional resources:

Additional Info

  • gcc version 11.1.0
  • -std=c99
koipond
  • 306
  • 2
  • 8
  • You can get rid of the size of the arrays in the array declarations in the parameters altogether. Arrays decay to pointers in parameters so there is no difference between `int x[5]`, `int x[]`, and `int* x`. – topoly Jun 02 '21 at 15:25
  • A 1D array as a function argument does not need a dimension. `void mat_print(size_t n, size_t m, double const mat[]);` If you are treating it as 2D array, then obviously you do need to pass the matrix dimensions. More generally, it is the outermost size of a true multi-dimensional array that is not needed. – Weather Vane Jun 02 '21 at 15:25
  • 2
    Does your sentence “`gcc` does not have the flag `-Wno-vla-parameter`” mean you did not pass the switch `-Wno-vla-parameter` to the `gcc` command? Because `gcc` does have that switch, and using it eliminates those error messages. – Eric Postpischil Jun 02 '21 at 15:45
  • 1
    @topoly: OP stated the reason they want to include the size in the declaration. – Eric Postpischil Jun 02 '21 at 15:46
  • 1
    @WeatherVane: OP stated the reason they want to include the size in the declaration. – Eric Postpischil Jun 02 '21 at 15:46
  • 1
    @topoly There is a difference between `int x[5]` and `int x[]`. See [makes the code's purpose clearer to human readers, but also makes static analysis easier](https://en.wikipedia.org/wiki/C2x#Features). – chux - Reinstate Monica Jun 02 '21 at 15:46
  • 1
    You can create a true 2D array dynamically: `double (*mat)[m] = calloc(n * sizeof *mat);` and define your function as `void mat_print(size_t n, size_t m, double const mat[restrict n][restrict m]);` – dbush Jun 02 '21 at 15:46
  • 1
    @EricPostpischil I see that now thanks, and OP boldened part of the question. – Weather Vane Jun 02 '21 at 15:47
  • 1
    @EricPostpischil Oh, you are right, `-Wno-vla-parameter` does exist, it is just not listet in `man gcc`. My bad, I just searched through the manual and did not try it... – koipond Jun 02 '21 at 15:47
  • @dbush I did not know this. Does this have any sort of performance implications? Also, the second `restrict` is incorrect, but this is maybe unimportant here – koipond Jun 02 '21 at 15:52
  • 1
    @koipond It shouldn't. My guess is it will be faster since the compiler knows how to best handle indexing a 2D array versus explicit multiplication by the user. – dbush Jun 02 '21 at 15:54
  • 1
    @koipond And the function definition would actually be `void mat_print(size_t n, size_t m, double const mat[restrict n][m]);` as `restrict` only applies to the outermost dimension. – dbush Jun 02 '21 at 15:55
  • @dbush What irritates me about the `calloc` call is that I would only be allocating memory for the row pointers, whereas the space for the rows themselves are a true VLA? If I am correct about this, then this might be a potential stackoverflow if `m` was really big (this might be a concern for me due to my application) – koipond Jun 02 '21 at 16:01
  • 1
    @koipond The entire space would be allocated in the heap. Specifically, you're allocating space for `n` objects of type `double [m]`. – dbush Jun 02 '21 at 16:04
  • 1
    this problem is even present if `static` and/or `restrict` is dropped. See https://godbolt.org/z/f6oeK1jfc. Is it some bug in the latest GCC? GCC 10.3 compiles without any warning – tstanisl Jun 02 '21 at 16:30
  • @dbush Ok I see, I missed `sizeof(*mat)`. This was exactly what I was looking for. One last thing though, I am getting an issue with `const` in the function parameters of `mat_print`. `warning: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]` This is because mat is originally not defined as `const`, I just want it to be inside of `mat_print`. Is there some clever way to get around this? – koipond Jun 02 '21 at 16:35
  • @tstanisl I don't know how that `static` made its way in there. That was not intended, but it also does not seem to be the issue for the original issue. Btw, `static` would be a promise to the compiler that `*mat` is not `NULL`. – koipond Jun 02 '21 at 16:44
  • 1
    @koipond not exactly. Parameter 'int A[static X]' means that A is a pointer to something with at least X ints. In the question 'X' is 'n*m' what could be 0. Thus NULL or '&arr[5]' from 'int arr[5]' could be used there – tstanisl Jun 02 '21 at 17:22

0 Answers0