tags:

views:

102

answers:

4

OK, I'm not entirely a newbie, but I cannot say I understand the following macro. The most confusing part is the division with value cast to size_t: what on earth does that accomplish? Especially, since I see a negation operator, which, as far as I know, might result in a zero value. Does not this mean that it can lead to a division-by-zero error? (By the way, the macro is correct and works beautifully.)

#define ARRAYSIZE(a) \
  ((sizeof(a) / sizeof(*(a))) / \
  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
#endif
+4  A: 

The first part (sizeof(a) / sizeof(*(a))) is fairly straightforward; it's dividing the size of the entire array (assuming you pass the macro an object of array type, and not a pointer), by the size of the first element. This gives the number of elements in the array.

The second part is not so straightforward. I think the potential division-by-zero is intentional; it will lead to a compile-time error if, for whatever reason, the size of the array is not an integer multiple of one of its elements. In other words, it's some kind of compile-time sanity check.

However, I can't see under what circumstances this could occur... As people have suggested in comments below, it will catch some misuse (like using ARRAYSIZE() on a pointer). It won't catch all errors like this, though.

Oli Charlesworth
I think it's there to catch an occasional misuse of the macro, like calling it on a pointer rather than an array. Unfortunately, it doesn't catch things like calling it on a pointer to an object that's the same size as the pointer.
John Calsbeek
The main circumstance I can think of is when `a` is a pointer, to something whose size isn't a factor of `sizeof (a)`. So most classes, you'd expect, but you won't get the desired division-by-zero if `a` is a `char*`. There are better ways to write an ARRAYSIZE helper in C++ so as to get a warning/error when it's used with a pointer. And this is C++ only, not dual-use C++ and C, because of the `static_cast`.
Steve Jessop
A: 

It does lead to a division-by-zero error (intentionally). The way that this macro works is it divides the size of the array in bytes by the size of a single array element in bytes. So if you have an array of int values, where an int is 4 bytes (on most 32-bit machines), an array of 4 int values would be 16 bytes.

So when you call this macro on such an array, it does sizeof(array) / sizeof(*array). And since 16 / 4 = 4, it returns that there are 4 elements in the array.

Note: *array dereferences the first element of the array and is equivalent to array[0].

The second division does modulo-division (gets the remainder of the division), and since any non-zero value is considered "true", using the ! operator would cause a division by zero if the remainder of the division is non-zero (and similarly, division by 1 otherwise).

Jengerer
A: 

suppose we have

T arr[42];

ARRAYSIZE(arr) will expand to (rougly)

sizeof (arr) / sizeof(*arr) / !(sizeof(arr) % sizeof(*arr))

which in this case gives 42/!0 which is 42

If for some reason sizeof array is not divisible by sizeof its element, division by zero will occur. When can it happen? For example when you pass a dynamically allocated array instead of a static one!

Armen Tsirunyan
+1  A: 

The division at the end seems to be an attempt at detecting a non-array argument (e.g. pointer).

It fails to detect that for, for example, char*, but would work for T* where sizeof(T) is greater than the size of a pointer.

In C++, one usually prefers the following function template:

typedef ptrdiff_t Size;

template< class Type, Size n >
Size countOf( Type (&)[n] ) { return n; }

This function template can't be instantiated with pointer argument, only array.

Limitations: doesn't work for array of local type, and doesn't yield compile time size.

For compile time size you can instead do like

template< Size n > struct Sizer { char elems[n]; };

template< class Type, size n >
Sizer<n> countOf_( Type (&)[n] );

#define COUNT_OF( a ) sizeof( countOf_( a ).elems )

Disclaimer: all code untouched by compiler's hands.

But in general, just use the first function template, countOf.

Cheers & hth.

Alf P. Steinbach