Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ARRAYSIZE C++ macro: how does it work?

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)))))
like image 984
Lajos Nagy Avatar asked Oct 31 '10 17:10

Lajos Nagy


2 Answers

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.

like image 63
Oliver Charlesworth Avatar answered Oct 15 '22 00:10

Oliver Charlesworth


I wrote this version of this macro. Consider the older version:

#include <sys/stat.h>
#define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))

int main(int argc, char *argv[]) {
  struct stat stats[32];
  std::cout << "sizeof stats = " << (sizeof stats) << "\n";
  std::cout << "sizeof *stats = " << (sizeof *stats) << "\n";
  std::cout << "ARRAYSIZE=" << ARRAYSIZE(stats) << "\n";

  foo(stats);
}

void foo(struct stat stats[32]) {
  std::cout << "sizeof stats = " << (sizeof stats) << "\n";
  std::cout << "sizeof *stats = " << (sizeof *stats) << "\n";
  std::cout << "ARRAYSIZE=" << ARRAYSIZE(stats) << "\n";
}

On a 64-bit machine, this code produces this output:

sizeof stats = 4608
sizeof *stats = 144
ARRAYSIZE=32
sizeof stats = 8
sizeof *stats = 144
ARRAYSIZE=0

What's going on? How did the ARRAYSIZE go from 32 to zero? Well, the problem is the function parameter is actually a pointer, even though it looks like an array. So inside of foo, "sizeof(stats)" is 8 bytes, and "sizeof(*stats)" is still 144.

With the new macro:

#define ARRAYSIZE(a) \
  ((sizeof(a) / sizeof(*(a))) / \
  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))

When sizeof(a) is not a multiple of sizeof(* (a)), the % is not zero, which the ! inverts, and then the static_cast evaluates to zero, causing a compile-time division by zero. So to the extent possible in a macro, this weird division catches the problem at compile-time.

PS: in C++17, just use std::size, see http://en.cppreference.com/w/cpp/iterator/size

like image 45
jorgbrown Avatar answered Oct 14 '22 23:10

jorgbrown