Niezawodnie określ liczbę elementów w tablicy

Każdy programista C może określić liczbę elementów w tablicy za pomocą tego dobrze znanego makra:

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

Oto typowy przypadek użycia:

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

Jednak nic nie stoi na przeszkodzie, aby programista przypadkowo przekazał wskaźnik zamiast tablicy:

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

W moim systemie to drukuje 2, ponieważ najwyraźniej wskaźnik jest dwa razy większy niż liczba całkowita. Myślałem o tym, jak zapobiec przypadkowemu przekazaniu wskaźnika przez programistę i znalazłem rozwiązanie:

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

Działa to, ponieważ wskaźnik do tablicy ma taką samą wartość jak wskaźnik do pierwszego elementu. Jeśli zamiast tego przekażesz wskaźnik, wskaźnik zostanie porównany ze wskaźnikiem do siebie, który prawie zawsze jest fałszywy. (Jedynym wyjątkiem jest rekurencyjny wskaźnik pustki, to znaczy pusty wskaźnik, który wskazuje na siebie. Mogę z tym żyć.)

Przypadkowe przekazanie wskaźnika zamiast tablicy powoduje teraz błąd w czasie wykonywania:

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

Ładny! Teraz mam kilka pytań:

Czy moje wykorzystanieassert jako lewy operand wyrażenia przecinka prawidłowy standard C? To znaczy, czy standard pozwala mi korzystaćassert jako wyrażenie? Przepraszam, jeśli to głupie pytanie :)

Czy sprawdzenie w jakiś sposób można wykonać w czasie kompilacji?

Mój kompilator C tak myśliint b[NUM_ELEMS(a)]; jest VLA. Jakikolwiek sposób, aby go przekonać?

Czy jestem pierwszym, który o tym pomyśli? Jeśli tak, ile dziewic mogę oczekiwać na mnie w niebie? :)

questionAnswers(2)

yourAnswerToTheQuestion