0

Forgive me for this possibly dumb question. Consider this:

int foo(int* arr) {
    std::cout << arr << "(" << sizeof(arr) << ")";
}

int main()
{
    int x[] = {0, 1, 2, 3, 4};
    foo(x);
    std::cout << " " << x << "(" << sizeof(x) << ")";
}

Output: 0x7c43ee9b1450(8) 0x7c43ee9b1450(20) - Same address, different size.

My understanding is that the function argument is an address specific to the first element of the array, so the size is 8 bytes, and the same should be true for the variable in main too; So how come the size of the variable outside of the function represent the whole array (4 bytes int times 5 elements = 20)? How could I possibly determine from inside the function how large an array actually is?

vandench
  • 1,973
  • 3
  • 19
  • 28
Komma
  • 13
  • 3
  • Inside `main`, `sizeof(x)` is the size of the array; in he other function, `sizeof(arr)` is the size of the pointer. – pmg Sep 07 '21 at 15:41
  • `arr` is a _pointer_ to a first item of an array, so you get a size of a pointer. `x` is an _array_, so you get a size of an array (size of an item multiplied by a number of items in the array). – CiaPan Sep 07 '21 at 15:41
  • Arrays of type `int[5]` decay into `int*` types very easily. That is what you are seeing here. – Martin York Sep 07 '21 at 15:42
  • "same address, different size" ... imagine a laser pointer pointing to a plane vs pointing to the plane pilot -- same address, different size :-) – pmg Sep 07 '21 at 15:43
  • Here's a C++ dupe: [When passing an array to a function in C++, why won't sizeof() work the same as in the main function?](https://stackoverflow.com/q/36525798) – 001 Sep 07 '21 at 15:44

2 Answers2

0

This is because the types are not the same inside and out side the function.

If you make sure the type is the same inside and outside the function you should get the same result.

int foo(int (&arr)[5])
{
     std::cout << arr << "(" << sizeof(arr) << ")";
     return 0;
}

The problem is that arrays decay into pointers at the drop of a hat. So if you pass an array to a function it will easily be converted into a pointer. That is what is happening here.

int foo(int* arr)
    //  ^^^^    Notice this is not an array.
    //          It is simply a pointer to an integer
    //          The array has decayed into a pointer to the
    //          first element in the array.
{
     std::cout << arr << "(" << sizeof(arr) << ")";
     return 0;
}

How could I possibly determine from inside the function how large an array actually is?

This is actually a real problem with C. In C they solved this by getting you to pass the size of the array as a second parameter:

 int foo(int* arr, std::size_t size);

Then call it from main as:

     foo(arr, sizeof(arr)/sizeof(arr[0])); // This always works as it done
                                           // at compile time and not runtime

In C++ we don't usually use C-arrays but prefer std::vector or std::array as the size is easily retrievable. Generally we use a container type C as they are duck types of Container:

 template<typename C>
 int foo(C& container)
 {
       std::cout << "(" <<container.size() << ")";
       return container.size();
 }
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thank you for clarifying it to me! It was pretty obvious after all, I'm just trying my best to wrap my head around this pointer stuff. – Komma Sep 07 '21 at 16:03
0

When passing an array like that you loose ALL the size information. So the called function is flying blind with regard to the array size.

In C++ is makes much more sense to use std::array (fixed size arrays), and std::vector (changing size arrays). It is much clearer what your intent is when you pass them to functions. There will be less mistakes and less memory access issues in your code.

I hope I don't scare you too much with the template version. The vector variant is more easy, but may use a bit more memory at runtime.

#include <array>
#include <vector>
#include <iostream>

template<size_t N>
size_t foo(const std::array<int,N>& arr) 
{
    for (const auto n : arr) std::cout << n << " ";
    std::cout << "(" << arr.size() << ")" << std::endl;
    return arr.size();
}

size_t foo(const std::vector<int>& arr)
{
    for (const auto n : arr) std::cout << n << " ";
    std::cout << "(" << arr.size() << ")" << std::endl;
    return arr.size();
}

int main()
{
    std::array<int,5> x{ 0, 1, 2, 3, 4 };
    std::vector<int> y{ 0, 1, 2, 3, 4 };

    auto size_x = foo(x);
    auto size_y = foo(y);
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19