Why is assigning to an array not allowed in C?

From ISO/IEC 9899:1999 on assignment operator constrains

§6.5.16 An assignment operator shall have a modifiable lvalue as its left operand.

Then on modifiable lvalue

§6.3.2.1 A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

Why not? probably because the array name decays to pointer to first element most probably.


However, an array assignment wrapped by a struct is allowed, as such:

//gcc 5.4.0

#include  <stdio.h>

struct A
{
    int arr[3];
    int b;
};

struct A foo()
{
    struct A a = {{1, 2, 3},10};
    return a;
}

int main(void)
{
    struct A b = foo();
    for (int i=0; i<3; i++)
          printf("%d\n",b.arr[i]);
    printf("%d\n", b.b);
}

Yields

1
2
3
10

tl;dr:

because C decided that arrays decay to pointers, and hasn't provided a way for the programmer to avoid it.

Long answer:

When you write

int arr[4];

from that moment on, every time you use arr in a dynamic context, C considers arr to be &arr[0], namely the decay of an array to a pointer (see also here and here).

Therefore:

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

is considered to be

&arr[0] = (int[]){0, 1, 2, 3};

which cannot be assigned. A compiler could implement a full array copy using memcpy(), but then C would have to provide a means to tell the compiler when to decay to a pointer and when not to.

Note that a dynamic context is different from a static context. sizeof(arr) and &arr are static context processed at compile time, in which arr is treated as an array.

Likewise, the initializations

int arr[4] = {0, 1, 2, 3};

or

int arr[] = {0, 1, 2, 3};

are static context - these initializations happen when the program is loaded into memory, before it even executes.

The language in the standard is:

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.

When an array is inside a struct, e.g.

struct s {
    int arr[4];
};
struct s s1, s2;

Then again using s1.arr is like &s1.arr[0], and it cannot be assigned.

However, while s1 = s2 is dynamic context, is not referencing the array. The compiler knows it needs to copy the full array, because it's part of the definition of the structure, and this assignment is generated implicitly. For example, if the compiler chooses to implement struct assignment using memcpy(), the array is automatically copied.