3

Array length can be calculated using *(&arr+1)-arr which then simplifies to (&arr)[1]-arr which further simplifies to 1[&arr]-arr.

But when the length is calculated in a function different from where memory allocation has been done, wrong results are computed.

For instance,

#include <iostream> 
#define ARRAY_SIZE(arr) (1[&arr]-arr)      
using namespace std;

void func(int *arr)
{
    cout<<ARRAY_SIZE(arr)<<endl;
}

int main()
{
    int arr[]={1,2,3,4,5};
    cout<<ARRAY_SIZE(arr)<<endl;
    func(arr);
}

This gives the output:

5
8

What accounts for such strange behaviour?

sudeepdino008
  • 3,194
  • 5
  • 39
  • 73

3 Answers3

5

Array length can be calculated using *(&arr+1)-arr

Only if arr is actually an array. Within func, arr is a pointer, so this dereferences a random word of memory to give undefined behavoiur.

There is no way to tell the size of an array given just a pointer to its first element. You could pass the array by reference:

template <size_t N>
void func(int (&arr)[N]) {
    cout<<ARRAY_SIZE(arr)<<endl;
    cout<<N<<endl;               // equivalent, and less weird
}

Using the same technique, we can reimplement ARRAY_SIZE without resorting to the preprocessor or any bizarre pointer arithmetic:

template <size_t N>
size_t ARRAY_SIZE(int (&arr)[N]) {
    return N;
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • In C++11, I would make that second one use a template T for the type, and make it constexpr to boot. C++03 can get the same effect (a constant expression that only works on arrays), but requires some `sizeof` [trickery](http://ideone.com/VzUndj). – Dave S Jul 30 '13 at 11:45
  • @DaveS In C++11 you would use `std::array` instead – nikolas Jul 30 '13 at 12:10
  • Actually, if `arr` is a pointer, what you get from the expression `*(&arr+1)-arr` is not its size but undefined behavior. The OP got the result of 8 which may seem a plausible value for the size of `int*`, but actually it is a mere coincidence. If the code was compiled without optimization and run on a x86, this is a very likely result because the expression `*(&arr + 1)` must have picked the caller's saved EBP. – ach Jul 30 '13 at 12:52
  • @AndreyChernyakhovskiy: You're right; I didn't think too hard about what that nonsense would do to a pointer. – Mike Seymour Jul 30 '13 at 13:01
3

In main() the compiler knows that arr is an array of size 5 * sizeof(int). In func() all the compiler knows is that arr is a pointer to a block of memory - it has no information about how big the array is, or even that it is an array (it could just be a chunk of memory allocated via malloc(), for example).

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
3

When you pass your array to a function, it decays to a pointer and knowledge of its size is lost. func can take an array of any size, so how the size be determined? You must either pass the size of the array as an extra parameter, or use a data structure such as std::vector or (C++11) std::array, which keep track of their size.

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91