Determinar com segurança o número de elementos em uma matriz

Todo programador C pode determinar o número de elementos em uma matriz com essa macro conhecida:

#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a])

Aqui está um caso de uso típico:

int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
printf("%lu\n", NUM_ELEMS(numbers));          // 8, as expected

No entanto, nada impede que o programador passe acidentalmente um ponteiro em vez de um array:

int * pointer = numbers;
printf("%lu\n", NUM_ELEMS(pointer));

No meu sistema, isso imprime 2, porque, aparentemente, um ponteiro é duas vezes maior que um inteiro. Pensei em como evitar que o programador passasse um ponteiro por engano e encontrei uma solução:

#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a]))

Isso funciona porque um ponteiro para uma matriz tem o mesmo valor que um ponteiro para seu primeiro elemento. Se você passar um ponteiro, o ponteiro será comparado com um ponteiro para si mesmo, o que é quase sempre falso. (A única exceção é um ponteiro vazio recursivo, isto é, um ponteiro vazio que aponta para si mesmo. Eu posso viver com isso.)

Acidentalmente, passar um ponteiro em vez de uma matriz agora dispara um erro durante a execução:

Assertion `(void*)&(pointer) == (void*)(pointer)' failed.

Agradável! Agora tenho algumas perguntas:

Meu uso deassert como o operando da esquerda da expressão vírgula válido padrão C? Ou seja, o padrão me permite usarassert como uma expressão? Desculpe se essa é uma pergunta boba :)

O cheque pode de alguma forma ser feito em tempo de compilação?

Meu compilador C acha queint b[NUM_ELEMS(a)]; é um VLA. Qualquer maneira de convencê-lo do contrário?

Eu sou o primeiro a pensar nisso? Se sim, quantas virgens posso esperar por mim no céu? :)

questionAnswers(2)

yourAnswerToTheQuestion