views:

184

answers:

5

I recently found some code like this:

typedef int TenInts[10];
void foo(TenInts &arr);

What can you do in the body of foo() that is useful, that you could not do if the declaration was:

void foo(int *arr);    // or,
void foo(int arr[]);   // or,
void foo(int arr[10]); // ?

I found a question that asks how to pass a reference to an array. I guess I am asking why.

Also, only one answer to "When is pointer to array useful?" discussed function parameters, so I don't think this is a duplicate question.

+6  A: 

one difference is that its (supposed to be) impossible to pass a null reference. SO in theory the function does not need to check if the param is null, wheras an int *arr param could be passed null.

pm100
Very good point, that hadn't occurred to me.
Dan
A: 

You can ensure that the function is only called on int arrays of size 10. That may be useful from a type-checking standpoint.

Dan
+10  A: 

The reference-to-array parameter does not allow array type to decay to pointer type. i.e. the exact array type remains preserved inside the function. (For example, you can use the sizeof arr / sizeof *arr trick on the parameter and get the element count). The compiler will also perform type checking in order to make sure the array argument type is exactly the same as the array parameter type, i.e. if the parameter is declared as a array of 10 ints, the argument is required to be an array of exactly 10 ints and nothing else.

In fact, in situations when the array size is fixed at compile-time, using a reference-to-array (or pointer-to-array) parameter declarations is the primary, preferred way to pass an array. The other variant (when the array type is allowed to decay to pointer type) are reserved for situations when it is necessary to pass arrays of run-time size.

For example, the correct way to pass an array of compile-time size to a function is

void foo(int (&arr)[10]); // reference to an array

or

void foo(int (*arr)[10]); // pointer to an array

The incorrect way would be to use a "decayed" approach

void foo(int arr[]); // pointer to an element
// Bad practice!!!

The "decayed" approach should be normally reserved for arrays of run-time size and is normally accompanied by the actual size of the array in a separate parameter

void foo(int arr[], unsigned n); // pointer to an element
// Passing a run-time sized array

In other words, there's really no "why" question when it comes to reference-to-array (or pointer-to-array) passing. You are supposed to use this method naturally, by default, whenever you can, if the array size is fixed at compile-time. The "why" question should really arise when you use the "decayed" method of array passing. The "decayed" method is only supposed to be used as a specialized trick for passing arrays of run-time size.

The above is basically a direct consequence of a more generic principle. When you have a "heavy" object of type T, you normally pass it either by pointer T * or by reference T &. Arrays are no exception from this general principle. They have no reason to be.

Keep in mind though that in practice it is often makes sense to write functions that work with arrays of run-time size, especially when it comes to generic, library-level functions. Such functions are more versatile. That means that often there's a good reason to use the "decayed" approach in real life code, Nevertheless, this does not excuse the author of the code from recognizing the situations when the array size is known at compile time and using the reference-to-array method accordingly.

AndreyT
Thank you, great answer.
Dan
... although I disagree with your last edit. An array is a "heavy" object, but you can't really pass an array by value, unless you put it in a struct or something.
Dan
A: 

You get more semantic meaning regarding what the function is expecting.

Pierreten
A: 

You can write a function template to find out the size of an array at compile time.

template<class E, size_t size>
size_t array_size(E(&)[size])
{
    return size;
}

int main()
{
    int test[] = {2, 3, 5, 7, 11, 13, 17, 19};
    std::cout << array_size(test) << std::endl; // prints 8
}

No more sizeof(test) / sizeof(test[0]) for me ;-)

FredOverflow
The `sizeof()` solutions gives a compile time constant, your template function doesn't (unless you add `constexpr` with C++0x). See e.g. here for a solution: http://stackoverflow.com/questions/1500363/compile-time-sizeof-array-without-using-a-macro/1500517#1500517
Georg Fritzsche