Given the following macro using _Generic
to determine whether the argument is an array (as opposed to a pointer):
#include <stdio.h>
#define IS_ARRAY(T) \
_Generic( &(T)[0], \
typeof(T) : 0, \
default : 1 \
)
int a[2];
int *p = a;
int main() {
printf( "IS_ARRAY(a) = %d\n", IS_ARRAY(a) );
printf( "IS_ARRAY(p) = %d\n", IS_ARRAY(p) );
}
and:
$ clang --version
clang version 17.0.6
$ clang -std=c2x a.c
I get:
a.c:15:33: warning: due to lvalue conversion of the controlling expression, association of type 'typeof ((a))' (aka 'int[2]') will never be selected because it is of array type [-Wunreachable-code-generic-assoc]
15 | printf( "IS_ARRAY(a) = %d\n", IS_ARRAY(a) );
| ^
a.c:7:5: note: expanded from macro 'IS_ARRAY'
7 | typeof( (T) ) : 0, \
| ^
1 warning generated.
Yet:
$ ./a.out
IS_ARRAY(a) = 1
IS_ARRAY(p) = 0
appears to work despite the warning. Is the warning wrong here?
What it means is that the first expression of _Generic
is converted as per "lvalue conversion" (new change in C17) and the generic association list following is examined against the converted value.
This was because in C11 it was ambiguous how to treat qualifiers inside _Generic
. Some compilers allowed types + qualifiers const int: 1, int: 2
etc as two separate cases but some didn't. So as a fix, C17 guarantees a lvalue conversion of the passed expression, so it can never become const int
in my example. Note how clang gives the same warning as you got if we attempt this:
#define IS_INT(x) _Generic((x), int: 1, const int: 1)
Nor can it become an array type for that matter, since arrays cannot be assigned/lvalue converted. The warning you get is for typeof(T)
which is an array type.
(&T)[0]
is not a correct fix because this will turn the array into a pointer, then into an array again, and then it will decay... and you get a int*
.
&(T)[0]
is not a correct fix because this will derefence the array, type int
, then you get an address of that, int*
.
Note precedence []
over unary &
.
What you can do however, is to take the address of the passed expression and check if it is a pointer to array.
And as another little trick, the type int (*)[]
(pointer to array of incomplete type) is compatible with every array pointer int (*)[n]
no matter n
(see "composite types" C17 6.2.7).
Fixed code, C23 conforming:
#define IS_ARRAY(T) \
_Generic( &(T), \
typeof(*T) (*)[] : true, \
default : false \
)
int a[2];
to this, then the array doesn't get "decayed" into a pointer to the first element but rather &(T)
turns it to int (*)[2]
.typeof(*T)
does however decay the array as usual and we end up with int
.int (*)[2]
is compatible with int (*)[]
so it was an array and with the correct element type.