In C11 you could use _Static_assert in conjunction with _Generic, but you'll also need to provide type info, which I see as a good thing as it provides extra granularity; you get the ability to assert based on element type, as well as whether it's an array or not from _Generic, and you get a nice friendly message from _Static_assert... For example:
assert_array_type.c:6:33: error: static assertion failed: "expected array of int; got (char[42]){0}"
assert_array_type.c:6:33: error: static assertion failed: "expected array of int; got (int *){0}"
These errors are produced by the following testcase, depending upon how you compile:
#define array_type(a, T) _Generic(a, T *:  _Generic(&a, T **:    0 \
                                                      , default: 1)\
                                                      , default: 0)
#define assert_array_type(a, T) _Static_assert(array_type(a, T), "expected array of " #T "; got " #a)
int main(void) {
    assert_array_type((int [42]){0}, int); // this should pass
#   if defined(TEST_POINTER_FAIL)
    assert_array_type((int  *  ){0}, int); // this should fail
#   endif
#   if defined(TEST_ELEMENT_FAIL)
    assert_array_type((char[42]){0}, int); // this should fail
#   endif
}
The  two testcases can observed by defining TEST_POINTER_FAIL and/or TEST_ELEMENT_FAIL, i.e.
- cc -std=c11 -D'TEST_POINTER_FAIL'should cause an assertion failure at compilation time due to the fact that a pointer is passed, rather than an array.
- cc -std=c11 -D'TEST_ELEMENT_FAIL'should cause an assertion failure at compilation time due to the fact that the array is meant to be an array of- int, rather than an array of- char.