11

For example,

int x[10];
int i = 0;
x = &i; //error occurs!

According to C - A Reference Manual, an array name cannot be an lvalue. Thus, x cannot be an lvalue. But, what is the reason the array name cannot be an lvalue? For example, why does an error occur in the third line?

davmac
  • 20,150
  • 1
  • 40
  • 68
Jin
  • 1,902
  • 3
  • 15
  • 26
  • What would be the use case of it being lvalue? It just doesn't make sense. It's a *name*. You can't change *name*.. – Eugene Sh. Aug 18 '16 at 15:24
  • 1
    @EugeneSh. I don't think that's what OP meant, but let's see.. OP please clarify, you don't mean that you want to dynamically change the name, do you? – harold Aug 18 '16 at 15:26
  • In case you wan't it to behave as a *pointer* (the only somewhat making sense meaning), whink what will happen to these 10-element buffer you have already statically allocated, once you change the array's pointer... – Eugene Sh. Aug 18 '16 at 15:28
  • @EugeneSh. If it were a lvalue since array name is converted to pointer type to int, I think array name can be used as a pointer variable! – Jin Aug 18 '16 at 15:29
  • @Jin, so what will happen to the allocated space once you modify it? If you want to use it as a pointer - assign it to a pointer variable and use it. There is just *no reason* to make it lvalue. – Eugene Sh. Aug 18 '16 at 15:29
  • Your question and your example don't match. Even if an array was a modifiable lvalue, your example assigns a pointer to it. An array is not a pointer; that's a type mismatch. Why do you expect to be able to assign a pointer value to an array? "an array name can be used as a pointer variable" - no. An array value decays to a pointer in certain contexts. An array is not a pointer. – davmac Aug 18 '16 at 16:03
  • @davmac As far as I know, an array type expression is converted to pointer type expression. So, that is not a type mismatch! – Jin Aug 18 '16 at 16:07
  • @davmac - The list of circumstances where an array *doesn't* decay is far far shorter than when it does (only when operand of `sizeof` or unary `&`). – Oliver Charlesworth Aug 18 '16 at 16:11
  • @Jin in certain contexts, an array is converted to a pointer value _that is not an lvalue_ (C99/C11 6.3.2.1 paragraph 3). How could it be an lvalue after being converted? There is no pointer object, just a pointer value. An lvalue designates an object. Do you understand what an lvalue is? – davmac Aug 18 '16 at 16:12
  • @OliverCharlesworth indeed, but is that really important here? – davmac Aug 18 '16 at 16:13
  • @davmac - In response to your first comment. Following the quote from the spec, `x` *is* a pointer type in the OP's code snippet. – Oliver Charlesworth Aug 18 '16 at 16:14
  • @davmac Oh so `x` is just a pointer type value which doesn't designate an object? So I guess that's why it's not a modifiable lvalue? – Jin Aug 18 '16 at 16:16
  • @Jin - yup, that's exactly it (same as in the example I gave in the comments below Vlad's answer). – Oliver Charlesworth Aug 18 '16 at 16:16
  • @OliverCharlesworth Oh I see! Thanks! – Jin Aug 18 '16 at 16:17
  • @Jin No. `x` designates an array. It only decays to a pointer value in the context in which you have used it (and in many other contexts). The decay results in a non-lvalue pointer value which is not stored in any object. The array designated by `x`, and the pointer value that it decays to, are two distinct values. `x` is an lvalue (though not a modifiable lvalue; your book is wrong, apparently) but the pointer is not. – davmac Aug 18 '16 at 16:17
  • @davmac Then to sum up, `x` itself is an array type which designates object while expression `x` is a pointer value which doesn't designate object? – Jin Aug 18 '16 at 16:19
  • @Jin No. `x` is an expression which designates an array. When used as part of a larger expression the array value (generally) is converted to a pointer. You are trying to explain it in terms which are incorrect. – davmac Aug 18 '16 at 16:21
  • @davmac I think `x` has two meanings. `x` is both an expression which designates an array object and an expression which evaluates the address of first element of array! Check this out. http://stackoverflow.com/questions/17687429/why-array-type-object-is-not-modifiable Read the part about definition of expression in the first answer! – Jin Aug 18 '16 at 16:30
  • In short: you cannot increment an array. What should `x++;` mean? What should it do? You cannot assign to an array: what should `x = val;` do ? [ or: `x+x`, or `x+a` ;-] – joop Aug 18 '16 at 16:32
  • @Jin I do not agree, `x` is an expression designating an array. From C11 6.5.1, paragraph 2: _An identifier is a primary expression, provided it has been declared as designating an object (in which case it is an lvalue)_. Can't be any more clear than that. It only becomes a pointer after conversion. – davmac Aug 18 '16 at 16:35
  • @davmac Based on your answer, I guess the expression `x` in the third line is converted to a pointer type while expression `x` is a primary expression which designates array object? – Jin Aug 18 '16 at 17:11
  • @Jin you make it sound as if there are two different `x` expressions but there is only one, in the third line. Yes, it is a primary expression and an lvalue which designates an array object. Yes, that lvalue is converted to a pointer during evaluation of the containing expression. – davmac Aug 18 '16 at 17:17
  • @davmac Oh the last sentence makes everything so clear! "... during evaluation" – Jin Aug 18 '16 at 17:21

4 Answers4

7

Your reference is incorrect. An array can be an lvalue (but not a modifiable lvalue), and an "array name" (identifier) is always an lvalue.

Take your example:

int x[10];
int i = 0;
x = &i; //error occurs!

Apply C11 6.5.1, paragraph 2:

An identifier is a primary expression, provided it has been declared as designating an object (in which case it is an lvalue) ...

We see that x is a primary expression and is an lvalue, because it has previously been declared as designating an array object.

However, the C language rules state that an array expression in various contexts, including the left-hand-side of an assignment expression, are converted to a pointer which points at the first element of the array and is not an lvalue, even if the array was. Specifically:

Except when it is the operand of the sizeof operator, the _Alignof 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.

(C11 6.3.2.1 paragraph 3).

The pointer which is the result of the conversion specified above is not an lvalue because an lvalue designates an object, and there is no suitable object holding the pointer value; the array object holds the elements of the array, not a pointer to those elements.

The example you use in your question implies that you understand that an array expression decays (is converted to) a pointer value, but I think you are failing to recognize that after the conversion, the pointer value and the array are two different things. The pointer is not an lvalue; the array might be (and in your example, it is). Whether or not arrays are lvalues in fact has no bearing on your example; it is the pointer value that you are trying to assign to.

If you were to ask instead: Why do arrays decay to pointers when they are on the left-hand-side of an assignment operator? - then I suspect that there is no particularly good answer. C just doesn't allow assignment to arrays, historically.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • I would say there is a good reason for them to not be assignable: it means there is no need to store a pointer (the address of the array) in the stack. When using local arrays, the starting address is known to be at a fixed offset from the frame pointer, so they can be used without keeping anything extra (besides the array elements themselves) in memory. What I do have a gripe with is that `arr` and `&arr` are equivalent, and I don't like the second one even being allowed. – Guido Oct 29 '19 at 19:15
  • 1
    @Guido, arrays are not pointers. They decay to pointers (usually). Embed array into a struct and it will behave as a legit l-value. C was historically designed this way to re-use code written in B language. – tstanisl Sep 03 '21 at 13:57
4

Array names are non-modifiable lvalues in C.:)

Arrays are named extents of memory where their elements are placed. So you may not substitute one extent of memory for another extent of memory. Each extent of memory initially allocated for an array declaration has its own unique name. Each array name is bounded with its own extent of memory.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • But isn't array type expression converted to pointer type expression? For example, in the third line of my code since `x` is an array expression it should be converted to pointer type expression. Thus, `x` is no longer name extents of memory but a pointer type. – Jin Aug 18 '16 at 15:50
  • @Jin - Sure. But you equally can't write (for example) - `int x = 1; int y = 2; &x = &y;`. – Oliver Charlesworth Aug 18 '16 at 15:51
  • @OliverCharlesworth Does that mean pointer type expression can't be lvalue? – Jin Aug 18 '16 at 16:02
2

An array is an lvalue, however it is a non-modifiable lvalue.

It most likely has to do with compatibility of types. For example, you can do this:

struct ss {
    char c[10];
};

...

struct ss s1 = { { "hello" } };
struct ss s2 = s1;

But not this:

char s1[10] = "hello";
char s2[10] = s1;
dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    I don't think it's about compatibility, rather the standard explicitly calls out that "A modifiable lvalue is an lvalue that does not have array type ...". – Oliver Charlesworth Aug 18 '16 at 15:34
  • No an array is not an *lvalue* 6.3.2.1/3: Except when it is the operand of the sizeof operator, the _Alignof 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. – Jean-Baptiste Yunès Aug 18 '16 at 16:07
  • @Jean-BaptisteYunès your quote does not show what you claim. In certain contexts an expression (which may be an lvalue) is _converted_ to an expression which is not an lvalue. That doesn't mean that the original expression is not an lvalue. – davmac Aug 18 '16 at 16:25
  • @Jean-BaptisteYunès That quote is regarding when the array is converted to a "pointer to type". In *that* case, an array is not an lvalue, but it still is when it is not converted. – dbush Aug 18 '16 at 16:55
  • Sorry guys, but reading again the sentence, that means to me that an array is not an lvalue. Is there a missing *when* somewhere? – Jean-Baptiste Yunès Aug 19 '16 at 09:51
  • @Jean-BaptisteYunès In section 6.3.2.2: "Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, **an lvalue that does not have array type** is converted to the value stored in the designated object" Also in 6.3.1.1: "A *modifiable lvalue* is **an lvalue that does not have array type**...". These phrases suggest that an array is an lvalue, but not a modifiable lvalue. – dbush Aug 19 '16 at 12:46
  • @Jean-BaptisteYunès the "missing _when_" is not missing: "Except *when* it is the operand ...". Also, as I said in the previous comment, the sentence says that (in most contexts) the array value is converted to another value (which is not an lvalue) - it does not say that the array is not an lvalue to begin with. – davmac Aug 23 '16 at 12:42
  • @Jean-BaptisteYunès for instance 6.3.1.8 talks about usual arithmetic conversions. If I have an `int a` and a `float b` and I add them together as in `a + b`, then "_If the corresponding real type of either operand is float, the other operand is converted, without change of type domain, to a type whose corresponding real type is float_" - so `a` is converted to `float`. But you wouldn't argue that an `int` is a `float` because of that, right? `a` is still an `int`, even though in this context its value will be converted to `float`. It is the same with conversion of arrays to pointers. – davmac Aug 23 '16 at 12:54
  • As not being english native I now understand my misinterpretation of 6.3.2.1. Thank you all for your remarks, it really helps me. @davmac: array is (non modifiable) lvalue converted to pointer type that is not a lvalue. – Jean-Baptiste Yunès Aug 23 '16 at 17:00
2

It's true that array names yield pointer values in many contexts. But so does the & operator, and you don't expect that to be assignable.

int i = 42;
int *j = malloc(sizeof *j);
&i = j; /* obviously wrong */

int a[] = {1,2,3};
&a[0] = j; /* also obviously wrong */
a = j; /* same as the previous line! */

So when learning the relationship between arrays and pointers, remember that a is usually the same as &a[0] and then you won't think lvalue-ness is an exception to the rule - it follows the rule perfectly.