Reputation:
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;
}
Upvotes: 0
Views: 109
Reputation: 36597
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;
}
Upvotes: 0
Reputation: 96053
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.
Upvotes: 0
Reputation: 16805
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;
}
Upvotes: 1
Reputation: 1587
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:
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.
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.
Upvotes: 0