Why can't arrays be passed as function arguments? Why can't arrays be passed as function arguments? arrays arrays

Why can't arrays be passed as function arguments?


Why can't arrays be passed as function arguments?

They can:

void foo(const int (&myArray)[5]) {   // `myArray` is the original array of five integers}

In technical terms, the type of the argument to foo is "reference to array of 5 const ints"; with references, we can pass the actual object around (disclaimer: terminology varies by abstraction level).

What you can't do is pass by value, because for historical reasons we shall not copy arrays. Instead, attempting to pass an array by value into a function (or, to pass a copy of an array) leads its name to decay into a pointer. (some resources get this wrong!)


Array names decay to pointers for pass-by-value

This means:

void foo(int* ptr);int ar[10]; // an arrayfoo(ar);    // automatically passing ptr to first element of ar (i.e. &ar[0])

There's also the hugely misleading "syntactic sugar" that looks like you can pass an array of arbitrary length by value:

void foo(int ptr[]);int ar[10]; // an arrayfoo(ar);

But, actually, you're still just passing a pointer (to the first element of ar). foo is the same as it was above!

Whilst we're at it, the following function also doesn't really have the signature that it seems to. Look what happens when we try to call this function without defining it:

void foo(int ar[5]);int main() {   int ar[5];   foo(ar);}// error: undefined reference to `func(int*)'

So foo takes int* in fact, not int[5]!

(Live demo.)


But you can work-around it!

You can hack around this by wrapping the array in a struct or class, because the default copy operator will copy the array:

struct Array_by_val{  int my_array[10];};void func (Array_by_val x) {}int main() {   Array_by_val x;   func(x);}

This is somewhat confusing behaviour.


Or, better, a generic pass-by-reference approach

In C++, with some template magic, we can make a function both re-usable and able to receive an array:

template <typename T, size_t N>void foo(const T (&myArray)[N]) {   // `myArray` is the original array of N Ts}

But we still can't pass one by value. Something to remember.


The future...

And since C++11 is just over the horizon, and C++0x support is coming along nicely in the mainstream toolchains, you can use the lovely std::array inherited from Boost! I'll leave researching that as an exercise to the reader.


So I see answers explaining, "Why doesn't the compiler allow me to do this?" Rather than "What caused the standard to specify this behavior?" The answer lies in the history of C. This is taken from "The Development of the C Language" (source) by Dennis Ritchie.

In the proto-C languages, memory was divided into "cells" each containing a word. These could be dereferenced using the eventual unary * operator -- yes, these were essentially typeless languages like some of today's toy languages like Brainf_ck. Syntactic sugar allowed one to pretend a pointer was an array:

a[5]; // equivalent to *(a + 5)

Then, automatic allocation was added:

auto a[10]; // allocate 10 cells, assign pointer to a            // note that we are still typelessa += 1;     // remember that a is a pointer

At some point, the auto storage specifier behavior became default -- you may also be wondering what the point of the auto keyword was anyway, this is it. Pointers and arrays were left to behave in somewhat quirky ways as a result of these incremental changes. Perhaps the types would behave more alike if the language were designed from a bird's-eye view. As it stands, this is just one more C / C++ gotcha.


Arrays are in a sense second-class types, something that C++ inherited from C.

Quoting 6.3.2.1p3 in the C99 standard:

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

The same paragraph in the C11 standard is essentially the same, with the addition of the new _Alignof operator. (Both links are to drafts which are very close to the official standards. (UPDATE: That was actually an error in the N1570 draft, corrected in the released C11 standard. _Alignof can't be applied to an expression, only to a parenthesized type name, so C11 has only the same 3 exceptions that C99 and C90 did. (But I digress.)))

I don't have the corresponding C++ citation handy, but I believe it's quite similar.

So if arr is an array object, and you call a function func(arr), then func will receive a pointer to the first element of arr.

So far, this is more or less "it works that way because it's defined that way", but there are historical and technical reasons for it.

Permitting array parameters wouldn't allow for much flexibility (without further changes to the language), since, for example, char[5] and char[6] are distinct types. Even passing arrays by reference doesn't help with that (unless there's some C++ feature I'm missing, always a possibility). Passing pointers gives you tremendous flexibility (perhaps too much!). The pointer can point to the first element of an array of any size -- but you have to roll your own mechanism to tell the function how big the array is.

Designing a language so that arrays of different lengths are somewhat compatible while still being distinct is actually quite tricky. In Ada, for example, the equivalents of char[5] and char[6] are the same type, but different subtypes. More dynamic languages make the length part of an array object's value, not of its type. C still pretty much muddles along with explicit pointers and lengths, or pointers and terminators. C++ inherited all that baggage from C. It mostly punted on the whole array thing and introduced vectors, so there wasn't as much need to make arrays first-class types.

TL;DR: This is C++, you should be using vectors anyway! (Well, sometimes.)