Search code examples
c++c

Why do array names not decay into pointers when left operand of assignment operator?


It is said in the C standard that

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 pointer to the initial element of the array object and is not an lvalue.

So why does the array type expression not decay into a pointer when used as the left operand of an assignment operator, like:

int arrayoften[] = { 10, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
arrayoften = 300; //not valid

I mean, the expression arrayoften is not one of the three exceptions, so shouldn't it theoretically be converted to a pointer?


Solution

  • One way of answering this (as @Rakete1111 noted in a comment) is to say that that an array on the lhs does decay to a pointer. But it's a pointer value (an rvalue), not an lvalue, so you can't assign to it.

    What's the difference between an rvalue and an lvalue? An rvalue is anything you can compute, that has a value. An lvalue is a value that additionally has a location (that the compiler knows), such that it can be stored into as the left-hand side of an assignment operator. (The term "rvalue" is therefore a backformation; it's something that can appear on the right-hand side.)

    We can understand this better with an example: Suppose I have

    int a[10], b[10];
    int *p1 = a, *p2 = b;
    
    p1 = p2 + 1;
    

    Now, the type p1 is "pointer to int", and the type of the expression p2 + 1 is "pointer to int", so I can make that assignment, no problem.

    But what if I said

    p1 + 1 = p2;    /* WRONG */
    

    The types on the left and right-hand sides are still both "pointer to int", but now the lhs is p1 + 1, which is an rvalue, but not an lvalue. It has no location, it's just a computed value, I can't assign to it.

    So, going back to your example, saying

    a = p1;
    

    is sort of the same thing. By the language in the C standard you quoted, the expression is converted to

    &a[0] = p1;
    

    Again, the types match, but &a1[0] is not an lvalue, so you can't assign to it.

    You can tell this is what's going on (or what might be going on) by looking at the compiler error messages: for one compiler I tried, the error was "illegal lhs of assignment operator", which I believe indicates that everything was fine until the compiler discovered that the lhs was not an lvalue. Indeed, I believe that the error message from Ritchie's original C compiler was simply "not an lvalue". (I should boot up my '11 and see.)

    Now, with all of this said, it's only one interpretation. That there are others is suggested by the error messages of other compilers:

    1. "incompatible types in assignment"
    2. "incompatible types when assigning to type ‘int[10]’ from type ‘int’"
    3. "array type 'int [10]' is not assignable"

    Compiler 1 is an older version of gcc, compiler 2 is a newer version of gcc, and compiler 3 is clang. It looks like these compilers are (for whatever reason) not blindly converting the array on the lhs to a pointer. Perhaps this is so that they can give a better error message, perhaps it's for some other reason, although it does seem to suggest that these compilers are adding another exception -- not explicitly listed in the Standard -- to the list of cases where arrays are not converted to pointers.

    [As an aside, though, I'm not sure what rule these newer compilers are using to cause the lhs to remain an array. It can't be, "don't convert arrays to pointers on the left-hand side of an assignment operator", because there are plenty of valid expressions -- such as *(a + 1) = 10 -- where that conversion is fine.]

    One more point this whole discussion brings up about the deep intertwinedness of the arrays-decay-to-pointers rule is that it makes it very difficult to contemplate a future in which there's an extension to C that adds proper array assignment. Any compiler that wants to make

    int a[10], b[10];
    a = b;
    

    work, first has to figure out how to not convert it into

    a = &b[0];
    

    or

    &a[0] = &b[0];