Making a function which can check the size of an array provided to it

Is this possible at all? I'm trying to write a function which I'd like to include in a personal library for future use, and as it's afunction I'd liketo be able to just use as and when, it would be really useful to be able to check the length of an array provided to it.

The syntax

uint8_t MyFunction(uint8_t ArrayProvided[]){

lets one feed in an array easily enough, but I know that behind the scenes it is using pointers, and that means that if inside the function you try to call sizeof on the array, you always get "2" as the results, which is the size of the pointer.

I know one can supply an array size to a function as a separate variable, but if you're trying to make a function really resilient for future use (especially if it might be someday called from inside another function which an array has already been fed in to) then this isn't great. That variable showing the function length has to be provided from somewhere, and makes using the function in different projects more work.

So is there a way a function can check for itself the size of an array given to it?
Thanks

When you pass an array to a function what you are really passing is a pointer to the array. As a result the sizeof() function used within a function to determine the number of elements in the array will not work because it will return the size of the pointer rather than the number of bytes used by the array

One possible way round this is to put an end marker in the array in the last array position and use this to determine the size of the array. However, this requires that the end marker value does not appear within the data held in the array. This is how C style strings work by being terminated by a '\0' character that is used by functions that act on the string, such as strlen()

even if there are several function calls between where the array is defined and where its size is needed, why can't the size be passed down thru all the intermediate functions along with the array?

then there's the question of the size of the array or how many elements of the array are used? either value can be passed down

Maybe add a delimitator string to the end of an array then iterate thru the array looking for the delimitator while counting the characters ? :thinking:

@UKHeliBob suggested this :face_with_spiral_eyes:

Thanks everyone, passing a size variable down through other functions ought to work, not as convenient, but ought to be workable. Unless you had something where the initial array to then be passed down was defined as having a size based on a variable supplied to it?

Why would that be a problem ?
By definition you already know the number of elements in the array

what if the first array element is the size?

I presume you want to have something like this?

template <class T, size_t n> size_t arraySize(T const (&)[n]) { return n; }

Usage:

int arr[] {1, 2, 3, 4};
Serial.println(arraySize(arr));  // Prints "4".

Can you please pass an example of the template being used ?

This is because a pointer instead of an array reference is passed to the arraySize function.

Maybe you can try to change the following,

to:

template <size_t n> void aFunc(int (& array)[n]) {

Then that function should be able to accept an array reference, e.g.,

template <size_t n> uint8_t myFunction(uint8_t (& arrayProvided)[n]) {
  // ...
}

When many different array types and/or sizes are used, one might want to use the following trick to avoid large binaries due to multiple template instantiations.

uint8_t myFunction_(uint8_t* arrayProvided, size_t size) {
  // ...
}

template <size_t n> uint8_t myFunction(uint8_t (& arrayProvided)[n]) {
  return myFunction_(arrayProvided, n);
}

No, it only handles references to arrays, not pointers.

Where would it get the size from? Dynamically allocated arrays do not store their size anywhere*.

The idea of passing arrays by pointers without any distinction was a mistake. The solution is to never let your arrays decay to a pointer in the first place.
In C++, you should pass arrays (both fixed-size and dynamic) to functions using a std::span.

(*) Usually, at least not for types with a trivial destructor.

I'd argue that for any use case, discarding the size is a bad idea, hence my suggestion of span.

C++ Core Guidelines: I.13: Do not pass an array as a single pointer

Although std::span requires C++20, you can write your own basic version using only Arduino-compatible C++11. As an example, a very limited implementation could look like this: https://godbolt.org/z/K414vc9s5

template <class T>
struct my_span {
    T *begin_ = nullptr;
    size_t size_ = 0;

    constexpr my_span() = default;
    constexpr my_span(T *begin, size_t size) : begin_{begin}, size_{size} {}
    template <size_t N>
    constexpr my_span(T (&array)[N]) : my_span{array, N} {}

    constexpr size_t size() const { return size_; }
    constexpr T *begin() const { return begin_; }
    constexpr T *end() const { return begin_ + size_; }
    constexpr T &operator[](size_t i) const { return begin_[i]; }
};

Then you just replace any (pointer to) array arguments to your functions by the span class:

void func(my_span<int> array_view) {
    println(array_view.size());
    for (int element : array_view)
        println(element);
}

int main() {
    int array[] {1, 2, 3};
    func(array);
}

There are more comprehensive alternatives, such as gsl::span (requires C++14 with standard library) or tcbrindle/span (requires C++11 with standard library).

When talking about function arguments, yes.
I'd argue that passing an array of unknown size to a function is almost always a mistake. If you don't know the size of an argument of array/pointer type, you cannot (safely) do anything useful with that array.
Usually, the size is either implicit, or passed as an extra parameter. Both options have shown to lead to countless bugs.

This still leaves room for disagreement between your constant and the actual size of the array you're working with:

constexpr int array_size = 3;
int foo[array_size];
int bar[2];

int your_function(int array[]) {
    return array[array_size - 1];
}

your_function(bar);     // bug
your_function(foo + 1); // bug

std::span supports arrays with compile-time constant sizes (in which case it only stores a pointer): std::span - cppreference.com

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.