r0n9
r0n9

Reputation: 2729

C++ the meaning of getting size of a array

Here is a macro for getting array size

#define array_size(array) \
(sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*)))

I think normally (sizeof( array ) / (sizeof( array[0] )) is good enough to get the size of the array.

I guess the part

(sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*)) 

is to avoid the whole thing divided by zero, anyone could help to explain?

Thanks in advance.

Cheers,

Upvotes: 8

Views: 494

Answers (3)

Juraj Blaho
Juraj Blaho

Reputation: 13451

I think normally (sizeof( array ) / (sizeof( array[0] )) is good enough to get the size of the array.

Although it is not your primary question, but you mentioned it. The correct way to determine array size in C++ is using templates:

template<typename T, size_t size>
constexpr size_t array_size(T(&)[size]){
    return size;
}

Upvotes: 6

Werner Henze
Werner Henze

Reputation: 16726

I assume this part is clear: sizeof( array ) / sizeof( array[0] )

This part (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*)) is a logical expression, so yielding true or false. When computing the whole expression, so multiplying sizeof( array[0] ) with the logical expression, the compiler converts the logical expression to 0 or 1. So you end up with
sizeof( array ) / (sizeof( array[0] ) * 1) or
sizeof( array ) / (sizeof( array[0] ) * 0).

The first case is the normal and desired case. The second case will give you a compiler error because of division by zero. So the code will not compile if you call for example:

long *p; // assuming a platform with sizeof(long)==sizeof(void*)
array_size(p);

But it will not catch errors like:

char *p;
array_size(p);

And it will fail to compile for a case I'ld want it to compile for:

long x[1]; // assuming a platform with sizeof(long)==sizeof(void*)
array_size(x);

BTW, if you are declaring this function as a macro (and in C++ I'ld really prefer the template style solution), you should change all array in the macro to (array).

Upvotes: 1

Daniel Fischer
Daniel Fischer

Reputation: 183858

Multiplying sizeof array[0] in the divisor by

(sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))

makes the divisor zero if

sizeof array == sizeof(void*)

and

sizeof array[0] > sizeof(void*)

In those cases, you get a division by zero during the compilation, which would cause the compilation to fail.

These checks are an attempt to detect arguments that are pointers (be they the result of array-to-pointer conversion or not), since one can't know how large an "array" a pointer points to by using that quotient.

It fails if other pointer types have different sizes than void*, and it doesn't detect pointers to things that are not larger than void*s. It probably does more harm than good by lulling the author in a false sense of security.

Upvotes: 12

Related Questions