1

I have a few reasons to define a type for a fixed length array such as this:

typedef float fixed_array_t[NX][NY];

I then want to pass references to fixed_array_t instances around to other functions. I'm getting a compiler warning from both GCC and CLANG though I'm seeing correct behavior.

What is this compiler warning telling me and how should my code be modified to avoid the warning? Bonus, why do I have to #define the array size? Compile-time constants apparently don't work. error: variably modified ‘fixed_array_t’ at file scope

Here is a small demonstration code:

#include <stdio.h>
#define NX 2  // also, why does const int NX = 2; not work?
#define NY 3
typedef float fixed_array_t[NX][NY];

void array_printer( const fixed_array_t arr )
{
    int i, j;
    for (i = 0; i < NX; i++ )
        for( j=0; j < NY; j++ )
            printf("Element [%d,%d]=%f\n", i,j, arr[i][j] );
}

int main( int argc, char ** argv )
{
    fixed_array_t testArray = { {1,2,3}, {4,5,6} };
    array_printer( testArray );
}

GCC warning:

warning: passing argument 1 of ‘array_printer’ from incompatible pointer type

CLANG warning (actually compiling equivalent code in OpenCL):

warning: incompatible pointer types passing 'fixed_array_t' (aka 'real [2][3]'), expected 'real const (*)[3]'

Yet program operation is fine:

Element [0,0]=1.000000
Element [0,1]=2.000000
Element [0,2]=3.000000
Element [1,0]=4.000000
Element [1,1]=5.000000
Element [1,2]=6.000000
NoahR
  • 1,417
  • 3
  • 18
  • 33
  • You need the `#define` because in C (unlike C++), a 'const int` is not really a fixed constant, so cannot be used to define array length. – Keith Nov 10 '11 at 02:46

3 Answers3

2

I think the reason this is a problem is that array_printer can pass what it thinks is const char (by reference via pointer), yet calling code has a non-const reference and hence can change the values pointed to.

Try changing:

void array_printer( const fixed_array_t arr )

to

void array_printer( fixed_array_t arr )
Keith
  • 6,756
  • 19
  • 23
  • I don't understand your explanation. Your solution works... no more compiler warning. This isn't ideal because I really want my **elements** of the fixed_array_t to be unmodifiable within `array_printer`. If I inadvertently do `arr[0][1]=1.0;` in `array_printer(...)`, I want the compiler to tell me about it. – NoahR Nov 10 '11 at 03:18
  • I don't think C provides any mechanisms to protect the contents of arrays or structures in the manner you'd like. – sarnold Nov 10 '11 at 03:33
  • @NoahR. See this http://stackoverflow.com/questions/4573349/c-function-const-multidimensional-array-argument-strange-warning – Keith Nov 10 '11 at 04:09
2

This is simply an unfortunate corner-case in C.

The type of the formal parameter const fixed_array_t arr is a synonym for const float (*arr)[XY], and the actual parameter totalArray evaluates to type float (*)[XY].

A pointer to array XY of float is simply not considered implicitly convertible to an pointer to array XY of const float. Perhaps it ought to be, but it isn't.

caf
  • 233,326
  • 40
  • 323
  • 462
0

If you just want the problem solved, you can use a struct to encapsulate your array:

$ cat struct.c ; make CFLAGS=-Wall -Wextra struct ; ./struct
#include <stdio.h>

#define NX 2
#define NY 3

typedef struct fixed {
    float arr[NX][NY];
} fixed_t;

void array_printer( const fixed_t f)
{
    int i, j;
    for (i = 0; i < NX; i++ )
        for( j=0; j < NY; j++ )
            printf("Element [%d,%d]=%f\n", i,j, f.arr[i][j] );
}


int main(int argc, char *argv[]) {
    fixed_t f = {.arr={ {1,2,3}, {4,5,6} }};
    array_printer(f);
    return 0;
}

cc -Wall    struct.c   -o struct
Element [0,0]=1.000000
Element [0,1]=2.000000
Element [0,2]=3.000000
Element [1,0]=4.000000
Element [1,1]=5.000000
Element [1,2]=6.000000

No warnings, no errors, and only slightly more annoying to use (f.arr[][] rather than arr[][] in the array_printer, for example).

If you amend the struct fixed to include the NX and NY dimensions, you could even have multiple different-sized objects in your program. (Though you'd slightly lose the benefits of compile-time-known bounds, I'm not sure how much that really buys you.)

sarnold
  • 102,305
  • 22
  • 181
  • 238
  • ok, annoying, but Ok. I'm definitely wanting the compile-time known bounds. I'm gonna try a few things with this in my real code. – NoahR Nov 10 '11 at 03:23
  • I was hoping to get some bounds checking from the compiler. Even with `-O2 -Wall` I don't get any warning about `f.arr[4][4]=1.0;` But, I don't get any help with regard to simple array types either. – NoahR Nov 10 '11 at 03:39
  • Thanks for your help. Not as pretty syntax, but this gets done everything I need. – NoahR Nov 10 '11 at 03:56
  • Compile-time bounds checking would be awesome; but the few attempts I've seen to add bounds checking to C tended to cut its performance in half _and_ fail to work with some programs that use negative offsets into array 'slices' (that's my inelegant wording). – sarnold Nov 10 '11 at 22:05
  • 1
    As presented this has quite different semantics - a temporary copy of the entire array is likely to be created each time the function is invoked. Instead you could pass a pointer to the structure, declaring the parameter as `const fixed_t *f`. – caf Nov 10 '11 at 23:41
  • Excellent point @caf; I took it for granted that using a `struct` in this fashion would make a new copy _and that this behavior was desirable_. Both points can well be wrong. :) – sarnold Nov 10 '11 at 23:45