1

In C we have pointers to arrays objects and arrays of constant known size.
The NetBSD defines an __arraycount macro, which is defined as #define __arraycount(x) (sizeof(x)/sizeof(x[0])).
I wouls like to define my own __arraycount macro, which will not compile when given a pointer to array. To do that, i need a __is_array_of_constant_known_size macro.
I would like to have a macro that is a compile time constant which for arrays of constant known size will return true/nonzero and for pointers to array objects (or any other pointers) will return false/zero.
This is what i've come up with:

#if __GNUC__
#define __is_array_of_constant_known_size(x)  ( __builtin_constant_p((void*)x == (void*)&x)?1:0 )
#else
#define __is_array_of_constant_known_size(x)  ( (void*)&x[0] == (void*)&x )
#endif

// __arraycount may look like this:
#define __arraycount(x)  ( sizeof(char[__is_array_of_constant_known_size(x) ? 1 : -1]) ? (sizeof(x)/sizeof(x[0])) : -1 )

// tests:
static char var1[5];
typedef char type1[__is_array_of_constant_known_size(var1)?1:-1];
static const int int1 = __is_array_of_constant_known_size(var1);
static const int sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)?1:-1]);

static char * var2;
typedef char type2[__is_array_of_constant_known_size(var2)?1:-1]; // should error
static const int int2 = __is_array_of_constant_known_size(var2);
static const int sizeof2 = sizeof(char[__is_array_of_constant_known_size(var2) 1:-1]); // should error

For GNUC compilers (clang6.0.0 and gcc8.1) tests are ok, ie. the lines containing type2 and sizeof2 fail to compile with error: size of array is negative.
However when GNUC is not defined, then i get:

<source>:8:1: warning: variably modified 'type1' at file scope
 typedef char type1[__is_array_of_constant_known_size(var1)?1:-1];
 ^~~~~~~
<source>:10:21: error: initializer element is not constant
 const int sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)?1:-1]);
                     ^~~~~~
<source>:13:14: error: variably modified 'type2' at file scope
 typedef char type2[__is_array_of_constant_known_size(var2)?1:-1];
              ^~~~~
<source>:4:47: error: initializer element is not constant
 #define __is_array_of_constant_known_size(x)  ( (void*)&x[0] == (void*)&x )
                                               ^
<source>:14:18: note: in expansion of macro '__is_array_of_constant_known_size'
 const int int2 = __is_array_of_constant_known_size(var2)?1:-1;
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:15:21: error: initializer element is not constant
 const int sizeof2 = sizeof(char[__is_array_of_constant_known_size(var2)?1:-1]);

The line int1 = __is_array_of_constant_known_size(var1) compiles without error. That means, that __is_array_of_constant_known_size(var1) is a constant expression, because

All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals from 6.7.8 in C99.

But the line sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)]) fails to compile with error: initializer is not compile time constant. Why is that?
Why (void*)&var1[0] == (void*)&var1 is a constant expression, but sizeof(char[((void*)&var1[0] == (void*)&var1)]) is not?
Is there a better way to implement compile time assertion inside the __arraycount macro rather then sizeof(char[__is_array_of_constant_known_size(var1)?1:-1])?

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
KamilCuk
  • 120,984
  • 8
  • 59
  • 111

0 Answers0