If arrays are passed by reference, why should I use int(&)[]? [duplicate]
Asked Answered
C

1

17

Consider:

#include <iostream>
using namespace std;

void Change(int arr[3]) {
    for (int i = 0; i < 3; i++) {
        arr[i] = 1;
    }
}

int Test() {
    int arr[3] = { 0, 0, 0 };

    for (int i = 0; i < 3; i++) {
        cout << arr[i] << endl;
    }

    Change(arr);

    for (int i = 0; i < 3; i++) {
        cout << arr[i] << endl;
    }

    return 0;
}

Since arrays are passed by default as a pointer to their first element and not copied, changing the value of an element of the array in a function effectively results in changing the value of that element of the array in the function caller, which is why the above code outputs

0
0
0
1
1
1

If this is the case, then why would anyone need to pass an array like the following?

void Change(int (&arr)[3])

I am aware that the parentheses are needed to make the argument a reference to an array instead of an array of references, but what do I gain?

Cleary answered 6/9, 2023 at 8:54 Comment(10)
You may would change the pointer to address a other array or array position? If this is not needed, there is no need to use a ref here.Folia
Use a const or mutable reference to std::vector or std::array and you will gain both readability and robustness.Medawar
Arguably int (&arr)[3] is worse. You can no longer pass std::array<int, 3> to it, nor a part of a larger array. Passing std::span<int, 3> would work around that, but the benefits over a plain pointer are questionable.Mill
@Mill Except the (fully-featured) alternative is not a plain pointer, it’s a pointer + size, and even then you are foregoing a compile-time assertion that the size is correct.Notarize
@KonradRudolph If you want the assertion, span is probably the better option, since it checks the size if given an array, but at the same time can be given a subarray or a larger array, or std::array.Mill
Re: "changing the value of an array" -- an array doesn't have a value; the code changes the value of an element of the array. Especially with C-style arrays you have to be careful not to confuse the array and its elements; the rules for C-style arrays make them interoperate in sometimes-confusing ways.Coolth
@PeteBecker You're right, I didn't have the 2 concepts mixed up in my mind when writing the question and I'm pretty sure everyone got what I meant. That said your phrasing is more correct and less confusing to newcomers, therefore I'll edit the question.Cleary
It's not that it "effectively results in changing the value of that element"; it simply does change that element. Objects, even with automatic storage duration, don't exist separately in each function that has a reference to them.Misreport
Related: https://mcmap.net/q/387705/-how-does-std-end-know-the-end-of-an-arrayGwendolin
(The question is based on a misconception; arrays are not "passed by reference". In fact, passing a pointer explicitly is also not passing by reference. A decayed pointer is passed by value; this has much the same consequences as the semantics seen in more modern languages where an object with reference semantics is passed by value.)Inglebert
S
43

This function declaration:

void Change(int arr[3])

is adjusted by the compiler to:

void Change(int *arr)

So, the function knows nothing about the size of the passed array.

If you declare the function like:

void Change(int ( &arr )[3])

then within the function, you can get the size of the array using, for example, the standard function std::size(), or get its first and last iterators like std::begin(arr) and std::end(arr). And moreover, you can pass the array by reference to a template function that accepts its argument by reference, similarly to passing any container.

Another advantage is that the compiler will check that an array of the required size is passed, and the user of the function can not pass a null pointer to the functon.

Sway answered 6/9, 2023 at 9:6 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.