0

In my file I have created function length that must return length of auto type array, but instead of right answer it everytime returns 1.

#include <iostream>
using namespace std;

int length(auto arr){
    return sizeof(arr) / sizeof(*arr);
}

int main(){
    int arr[] = {1,2,3,4,5,0};
    // Test
    cout << length(arr); // it returns 1 but the right answer is 6
    return 0;
}
  • 7
    Why not use c++'s [`std::array`](https://en.cppreference.com/w/cpp/container/array#:~:text=std%3A%3Aarray%20is%20a,t%20decay%20to%20T*%20automatically.) which has methods for finding its size – Imanpal Singh Sep 08 '20 at 06:26
  • 1
    If your compiler's up to date, `std::size` If not, `sizeof(arr)/sizeof (arr[0])` size of the whole array in bytes divided by size of a single element in bytes. Also consider using the `std::array` container. It's a smarter array, knows its size, and can be passed by value. – user4581301 Sep 08 '20 at 06:26
  • user4581301 it is returning the same answer: 1. –  Sep 08 '20 at 06:27
  • 1
    does this compile `int length(auto arr)` ?? – artm Sep 08 '20 at 06:27
  • artm yes it is compiling and is printing out 1 –  Sep 08 '20 at 06:28
  • 1
    Some interesting links might be [What is array to pointer decay](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay) (That's why you get 1) and [Passing an array by reference](https://stackoverflow.com/questions/5724171/passing-an-array-by-reference) (that's how you can keep the dimension of the array. – Lukas-T Sep 08 '20 at 06:29
  • churill it is compiling, maybe you are using old compiler c++ 10 or older and the auto keyword was not realeased –  Sep 08 '20 at 06:32
  • 1
    @someseeapeniseeaharpoon Not sure why, but it doesn't compile on CLang or MSVC. Works with G++ however, so ok, let's say it compiles. Anyway, do any of the links help you? – Lukas-T Sep 08 '20 at 06:34
  • churill I am going to use std::vector or std::array –  Sep 08 '20 at 06:36
  • 1
    Ack. Yeah. You need to get the size of the array before it [decays into a pointer](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay).It's too late by the time you pass it into a function. – user4581301 Sep 08 '20 at 06:37
  • user4581301 maybe you are right, I am a beginner –  Sep 08 '20 at 06:39
  • @user4581301 That only works if the array's definition is visible in the current scope. In a called function, it isn't. – user207421 Sep 08 '20 at 06:49

4 Answers4

1

You are using a C-style array, and this type cannot be copied. If we assume that your compiler has a version that supports auto parameters, it's like if it had a template parameter that would be deduced to int * here, because instead of being copied, a C-style array decays to int *.

In this case, sizeof(arr) is sizeof(int *) which is probably 4 on a 32-bit system or 8 on a 64-bit system. sizeof(*arr) is sizeof(int) and is probably 4 on most systems. Thus sizeof(arr)/sizeof(arr[0]) in length() will probably always give 1 or 2.

If you want such a function returning the number of elements of an array, you could use the std::array() type as many comments suggest.

An alternative is to provide a template function that is aware of the constant (known at compile time) size of the array.

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <iostream>

template<typename T,
         int N>
int
length([[maybe_unused]] const T(&arr)[N])
{
  return N;
}

int 
main()
{
  int arr[] = {1,2,3,4,5,0};
  std::cout << length(arr) << '\n'; 
  return 0;
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30
0

Ah yes, you've fallen victim to the classic array decay problem.

When have a function that takes in an array, C passes it as a pointer, because "array" isn't a passable data type. You get 1 because sizeof(arr) == sizeof(size_t) == sizeof(*arr) == sizeof(int), so sizeof(arr) / sizeof(*arr) == 1.

Unfortunately, there is no way to find the length of C-style arrays in a subfunction. However, some things you can do:

  1. You can pass the length into the function as well. Unfortunately, this is pretty self-defeating if you want a length function. However, it's used for many C-style applications which need to support null bytes.

  2. You can use the C++ style std::array, which allows you to find the size of the array with size(). This allows you to create an array without having to play with C shenanigans. You can also use std::vector, which is variable-size instead of fixed-size.

id01
  • 1,491
  • 1
  • 14
  • 21
  • 1
    c++ is type of headache :( –  Sep 08 '20 at 06:34
  • 1
    You can also use `std::vector` which is likely to be a better replacement than `std::array` – john Sep 08 '20 at 06:34
  • 2
    @someseeapeniseeaharpoon This is a headache that C++ inherits from C. Unfortunately many people still see C++ as C with a few extra bits instead of thinking of it as an new language. Using arrays in C++ is not generally recommended. – john Sep 08 '20 at 06:35
  • 1
    *"there is no way to find the length of C-style arrays in a subfunction"* It's possible, look at `std::size`. – HolyBlackCat Sep 08 '20 at 06:42
  • @HolyBlackCat nope, just tried it. `int length(int a[]) { return std::size(a); }` gives us `error: no matching function for call to 'size(int*&)'` – id01 Sep 08 '20 at 06:50
  • @id01 It will work if the array is passed by reference. What I'm saying is that `std::size` itself is a function, and it does manage to obtain the array length, so it's possible. – HolyBlackCat Sep 08 '20 at 06:50
  • @HolyBlackCat That is true, but it requires specialized circumstances for it to be possible. One regular array pass and you're screwed. – id01 Sep 08 '20 at 06:54
0

It's impossible to pass an array to a function directly (by value, that is), attempting to do so passes a pointer to its first element instead. Other answers have already explained this.

What you can do is pass a reference:

int length(const auto &arr)
{
    return sizeof(arr) / sizeof(*arr);
}

This works for arrays, but gives wrong results for pointers, standard containers, etc.

To make it more bullet-proof, you can rewrite it to make it accept references to arrays only:

template <typename T, std::size_t N>
std::size_t length(const T (&)[N])
{
    return N;
}

But we already have a standard function that does exactly this, it's called std::size. And it's also overloaded to work with containers.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
0

The fact your code compiles at all is because your compiler (gcc?) supports a non-standard extension.

You would be better off using standard containers (e.g. std::vector<int> if the size is determined at run time, or std::array<int, 6> if the size is fixed at compile time).

But, for a function that takes a raw array and gives its size, you can simply pass a reference;

 int length(const auto &arg) {return sizeof(arr)/sizeof(*arr);}

or

 template<int N> int length(const int (&arr)[N])
 {
     return N;
 }

Depending on your needs, the function can also be made constexpr and noexcept.

In C++17 and later, simply use the helper function std::size() (supplied in various standard headers, such as <iterator>, and works with a raw array as well as standard containers)

int main(){
    int arr[] = {1,2,3,4,5,0};
    cout << std::size(arr); 
    return 0;
}
Peter
  • 35,646
  • 4
  • 32
  • 74