Why can't we pass arrays to function by value?

The origin is historical. The problem is that the rule "arrays decay into pointers, when passed to a function" is simple.

Copying arrays would be kind of complicated and not very clear, since the behavior would change for different parameters and different function declarations.

Note that you can still do an indirect pass by value:

struct A { int arr[2]; };
void func(struct A);

Here's another perspective: There isn't a single type "array" in C. Rather, T[N] is a a different type for every N. So T[1], T[2], etc., are all different types.

In C there's no function overloading, and so the only sensible thing you could have allowed would be a function that takes (or returns) a single type of array:

void foo(int a[3]);  // hypothetical

Presumably, that was just considered far less useful than the actual decision to make all arrays decay into a pointer to the first element and require the user to communicate the size by other means. After all, the above could be rewritten as:

void foo(int * a)
{
  static const unsigned int N = 3;
  /* ... */
}

So there's no loss of expressive power, but a huge gain in generality.

Note that this isn't any different in C++, but template-driven code generation allows you to write a templated function foo(T (&a)[N]), where N is deduced for you -- but this just means that you can create a whole family of distinct, different functions, one for each value of N.

As an extreme case, imagine that you would need two functions print6(const char[6]) and print12(const char[12]) to say print6("Hello") and print12("Hello World") if you didn't want to decay arrays to pointers, or otherwise you'd have to add an explicit conversion, print_p((const char*)"Hello World").


Answering a very old question, as Question is market with C++ just adding for completion purposes, we can use std::array and pass arrays to functions by value or by reference which gives protection against accessing out of bound indexes:

below is sample:

#include <iostream>
#include <array>

//pass array by reference
template<size_t N>
void fill_array(std::array<int, N>& arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        arr[idx] = idx*idx;
}

//pass array by value
template<size_t N>
void print_array(std::array<int, N> arr){
    for(int idx = 0; idx < arr.size(); ++idx)
        std::cout << arr[idx] << std::endl;
}

int main()
{
    std::array<int, 5> arr;
    fill_array(arr);
    print_array(arr);
    //use different size
    std::array<int, 10> arr2;
    fill_array(arr2);
    print_array(arr2);
}

The reason you can't pass an array by value is because there is no specific way to track an array's size such that the function invocation logic would know how much memory to allocate and what to copy. You can pass a class instance because classes have constructors. Arrays do not.


Summery:

  1. Passing the Address of the array's first element &a = a = &(a[0])
  2. New Pointer (new pointer, new address, 4 bytes, in the memory)
  3. Points to the same memory location, in different type.

Example 1:

void by_value(bool* arr) // pointer_value passed by value
{
    arr[1] = true;
    arr = NULL; // temporary pointer that points to original array
}

int main()
{
    bool a[3] = {};
    cout << a[1] << endl; // 0
    by_value(a);
    cout << a[1] << endl; // 1 !!! 
}

Addresses:

[main] 
     a = 0046FB18 // **Original**
     &a = 0046FB18 // **Original**
[func]
     arr = 0046FB18 // **Original**
     &arr = 0046FA44 // TempPTR
[func]
     arr = NULL
     &arr = 0046FA44 // TempPTR

Example 2:

void by_value(bool* arr) 
{
    cout << &arr << arr; // &arr != arr
}

int main()
{
    bool a[3] = {};
    cout << &a << a; // &a == a == &a[0]
    by_value(arr);
}

Addresses

Prints: 
[main] 0046FB18 = 0046FB18
[func] 0046FA44 != 0046FB18

Please Note:

  1. &(required-lvalue): lvalue -to-> rvalue
  2. Array Decay: new pointer (temporary) points to (by value) array address

readmore:

Rvalue

Array Decay