Поскольку вы пометили это как C ++, стоит упомянуть, что есть несколько лучший способ, чем макрос в стиле C:
template <class T, size_t N>
size_t countof(const T &array[N]) { return N; }
Это имеет то преимущество, что если вы случайно попытаетесь передать ему что-то, кроме массива, код просто не скомпилируется (тогда как передача указателя на макрос C скомпилирует, но даст плохой результат. Недостатком является то, что это не дает вам константу времени компиляции, поэтому вы не можете сделать что-то вроде этого:
int a[20];
char x[countof(a)];
В C ++ 11 или новее вы можете добавить constexpr
, чтобы получить постоянную времени компиляции:
template <class T, size_t N>
constexpr size_t countof(const T &array[N]) { return N; }
Если вы действительно хотите поддерживать то же самое на старых компиляторах, есть способ, изначально изобретенный Иваном Джонсоном, AFAIK:
#define COUNTOF(x) ( \
0 * sizeof( reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(x) ) + \
0 * sizeof( ::Bad_arg_to_COUNTOF::check_type((x), &(x)) ) + \
sizeof(x) / sizeof((x)[0]) )
class Bad_arg_to_COUNTOF
{
public:
class Is_pointer;
class Is_array {};
template<typename T>
static Is_pointer check_type(const T*, const T* const*);
static Is_array check_type(const void*, const void*);
};
При этом используется sizeof (x) / sizeof (x [0]) для вычисления размера, как это делает макрос C, поэтому он дает константу времени компиляции. Разница в том, что он сначала использует шаблонную магию, чтобы вызвать ошибку компиляции, если то, что вы передали, не является именем массива. Это происходит путем перегрузки check_type для возврата неполного типа для указателя, но полного типа для массива. Затем (действительно сложная часть) он вообще не вызывает эту функцию - он просто принимает размер типа, который должна возвращать функция, который равен нулю для перегрузки, которая возвращает полный тип, но не разрешается (форсируя ошибка компиляции) для неполного типа.
IMO, это довольно крутой пример шаблонного метапрограммирования - хотя, честно говоря, результат вроде бессмысленный. Вам действительно нужен этот размер в качестве постоянной времени компиляции, если вы используете массивы, которых в любом случае следует избегать. Используя std::vector
, можно указывать размер во время выполнения (и изменять размер вектора, когда / при необходимости).