Every C programmer can determine the number of elements in an array with this well-known macro:
#define NUM_ELEMS(a) (sizeof(a)/sizeof 0[a])
Here is a typical use case:
int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
printf("%lu\n", NUM_ELEMS(numbers)); // 8, as expected
However, nothing prevents the programmer from accidentally passing a pointer instead of an array:
int * pointer = numbers;
printf("%lu\n", NUM_ELEMS(pointer));
On my system, this prints 2, because apparently, a pointer is twice as large as an integer. I thought about how to prevent the programmer from passing a pointer by mistake, and I found a solution:
#define NUM_ELEMS(a) (assert((void*)&(a) == (void*)(a)), (sizeof(a)/sizeof 0[a]))
This works because a pointer to an array has the same value as a pointer to its first element. If you pass a pointer instead, the pointer will be compared with a pointer to itself, which is almost always false. (The only exception is a recursive void pointer, that is, a void pointer that points to itself. I can live with that.)
Accidentally passing a pointer instead of an array now triggers an error at runtime:
Assertion `(void*)&(pointer) == (void*)(pointer)' failed.
Nice! Now I have a couple of questions:
Is my usage of
assert
as the left operand of the comma expression valid standard C? That is, does the standard allow me to useassert
as an expression? Sorry if this is a dumb question :)Can the check somehow be done at compile-time?
My C compiler thinks that
int b[NUM_ELEMS(a)];
is a VLA. Any way to convince him otherwise?Am I the first to think of this? If so, how many virgins can I expect to be waiting for me in heaven? :)