5

There are three ways to pass an array to a function

1.

void changeArray(int array[]) {
    array[0] = 1111;
}
void changeArrayByPointer(int * array) {
    array[0] = 1111;
}
void changeArray(int (&array)[]) {
    array[0] = 1111;
}

I understood that 1 and 2 are practically the same, passing a pointer to the first element.

But what about passing by reference int (&array)[]?

What is the difference? When would you use int (&array)[]?

Initially, I thought that that's the syntax to explicitly pass an array by reference, so the programmer knows that the data is not copied (as all other data types are passed by a reference without creating a copy).

7
  • " When would you use int (&array)[]?..." When you want to pass an array(by reference) without being able to iterate through it. Commented Aug 20, 2024 at 4:24
  • Note also that int (&array)[] is only valid from c++20. So the question is basically, why c++20 allowed this usage. Commented Aug 20, 2024 at 4:25
  • @john Yeah, we can remove it. Commented Aug 20, 2024 at 4:31
  • 6
    @AmmarHoque No, int (&array)[] and int& array are not same in c++. See demo. Commented Aug 20, 2024 at 4:31
  • 2
    Not sure why this question got a down vote. It's an interesting question about which I knew nothing. I'm looking forward to an answer. Commented Aug 20, 2024 at 4:31

3 Answers 3

6

The difference is that the third form is more restrictive, while the first and second forms could benefit from a null check.

The first (and second) form can accept an argument that is not an array, as long as the argument converts to an int pointer.

void changeArray(int array[]) {
    array[0] = 1111;
}

int main() {
    changeArray(nullptr); // Causes a segmentation fault, but does compile.
}

The third form cannot.

void changeArray(int (&array)[]) {
    array[0] = 1111;
}

int main() {
    // changeArray(nullptr);
    // error: invalid initialization of non-const reference of type 'int (&)[]'
    //        from an rvalue of type 'std::nullptr_t'
}

An argument that is an array (of int) would be accepted by any of these functions.


I understood that 1 and 2 are practically the same, passing a pointer to the first element.

Not just practically the same. They are the same. Presumably, that is why you named the second function changeArrayByPointer instead of changeArray, since otherwise you'd get a function redefinition error. (If you want to test this out, try putting the declaration void changeArray(int array[]); before your main function and the definition void changeArray(int *array) { array[0] = 1111; } after. The definition links to the declaration, despite looking different.) See also Difference between passing array, fixed-sized array and base address of array as a function parameter.

In contrast, the first and third forms are distinct signatures, so they can overload the name changeArray. (However, if both overloads are in scope, trying to call one with an array argument is ambiguous.)

Sign up to request clarification or add additional context in comments.

Comments

6

When would you use int (&array)[]?

void changeArray(int (&array)[]) is more restrictive in that it only accepts arrays by reference. You can pass as argument(by reference) an array of any size with elements of type int. This also prevents one from passing a null pointer.

This was allowed through p0388r4 and cwg393 that mentions the rationale. They mention:

Motivation and impact

As of core issue 393,CWG393 function parameters can be pointers or references to arrays of unknown bound. However, binding such a parameter to an array of known bound isn't permitted:

void f(int(&)[]);
int arr[1];

f(arr);          // Error
int(&r)[] = arr; // Error

This restriction is unjustified and should be removed. One consequence of our approach is the fact that *

Comments

5

(3) also passes a pointer internally (references are implemented as pointers on all platforms I know of).

But unlike (1) and (2), it only accepts arrays, not arbitrary pointers.

Like (1) and (2), it doesn't preserve the length information.


I'm not sure why would anybody want to use (3). It seems to be a strictly worse alternative to (1) and (2), because you no longer can pass subarrays, standard containers, etc.

8 Comments

SFINAE, probably?
"I'm not sure why would anybody want to use (3)." -- It prevents passing in a null pointer, so there is no need to check for null.
@JaMiT "prevents passing in a null pointer" is a good point. Regarding having to check for null pointers, I believe this is a misconception. You don't have to check for null pointers either way, you can just crash. Some C standard library functions do this, there's nothing wrong with it.
@HolyBlackCat "You don't have to check for null pointers either way, you can just crash." -- True enough. It depends on what your requirements are.
The reason I bring is up is because I have coworkers who will use references instead of pointers in a parameters struct, "because then we don't have to check for null pointers". And then when I need to copy the struct with one pointer/ref modified, tell me to manually do elementwise copy using aggregate init. >:o
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.