Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is assigning to an array not allowed in C? [duplicate]

If I write:

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

Then my compiler (in this case, GCC) will complain that I don't have an expression before {. So I need to use a compound literal, fine:

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

And now we see that I'm not allowed to assign a value to an array.

What?

I can "circumvent" this with something like memcpy(some_arr, (int[]){0, 1, 2, 3}, sizeof(int[4])), or by assigning to each element of some_arr one-by-one (or through a loop.) I can't imagine that GCC is incapable of parsing the individual assignments from what I've wrote (a lazy compiler that doesn't care about the user could probably even do it in the pre-processor), so it seems to come down to "the standard said no." So why does the standard say this particular thing is off-limits?

I'm not looking for the language in the standard that says it's not allowed as much as I'm looking for the history lesson of how that part of the standard came to be.

like image 759
c-x-berger Avatar asked Feb 07 '20 08:02

c-x-berger


2 Answers

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
like image 96
Tony Tannous Avatar answered Sep 22 '22 05:09

Tony Tannous


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.

like image 30
root Avatar answered Sep 19 '22 05:09

root