0

There's a pretty extensive description of lvalue in What are rvalues, lvalues, xvalues, glvalues, and prvalues?, but this is geared towards c++ and I think a lot of those are not applicable to C.

My understanding of an Lvalue would be as follows:

  • Arrays are not, but their subscript values may be, as long as those are not an array. For example:

    int arr[3][2];
    arr       // no --> int[3][2]
    arr[1]    // no --> int[2]
    arr[1][1] // yes --> int
    
    
  • Structs can be directly or through member access, such as . or ->, as long as they don't evaluate to an array. For example:

    struct Member {int id};
    Member member = {.id=2}, *member_ptr = &member;
    member = {.id=3};    // ok
    member_ptr = &member; // ok
    member.id=3; // ok
    member_ptr->id=4; // ok
    
  • The address-of & operator can not. For example:

    a = 7;
    b = &a;
    &b = &a; // invalid
    
  • The value-of * operator can be as long as it doesn't refer to an array.

    int x[2][2] = {{1,2}, {3,4}};
    *(x+1)=3;        // not ok, evaluates to x[2]
    *(*(x+1)+1) = 3; // ok
    

Other than more obvious cases such as doing 7=x, are there main items that I'm missing in my understanding? Or any items that are incorrect in the above?

David542
  • 104,438
  • 178
  • 489
  • 842
  • You might note that you can't assign to functions, and you can only call functions via function pointers — you can't assign to a dereferenced function pointer. – Jonathan Leffler Feb 07 '21 at 03:27

1 Answers1

2

The C standard defines an lvalue in section 6.3.2.1p1 as follows:

An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. 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.

This definition includes arrays, however an array is not a modifiable lvalue.

The result of the indirection operator * is also an lvalue as it refers to an object. This also applies if the resulting object is an array.

The array subscript operator [] also results in an lvalue as x[y] is exactly equivalent to *(x + y).

The result of the member access operator . and pointer-to-member operator -> is also an lvalue.

A compound literal is an lvalue as well. For example, the following is valid:

int *p = (int [3]){1,2,3};
p[0] = 4;
dbush
  • 205,898
  • 23
  • 218
  • 273
  • what does an "incomplete type" mean? Also, what do you mean by a `computer literal is an lvalue` -- in the above example it's just assigned to `int *p` right? Isn't the compound literal on the right? – David542 Feb 07 '21 at 03:33
  • @David542 A type that is not fully defined, for example a struct that is declared but not defined. You can have a pointer to such a struct but you can't dereference it. In the example `p` points to the compound literal, and dereferencing `p` gives you a member of the array. – dbush Feb 07 '21 at 03:35
  • I see, so something like `typedef struct Item Item` ? – David542 Feb 07 '21 at 03:37
  • @David542 Yes, or even just `struct Item;` – dbush Feb 07 '21 at 03:37
  • I see, thanks for the feedback. By the way, I get an incompatible pointer error on `int *p = &(int [3]){1,2,3};`, but it works without the address-of: `int *p = (int [3]){1,2,3};`. – David542 Feb 07 '21 at 03:41
  • @David542 Yes, that shouldn't have been there. Had `p` been defined as `int (*p)[3]` it would have worked. – dbush Feb 07 '21 at 03:43
  • 1
    I'm not sure what "This definition includes arrays" means. I find it useful to think of "array" as an adjective, not a noun. An array object, for example, is not an lvalue because an object is not an expression. An array name used as an expression is not an lvalue if it undergoes "decay" to a pointer expression, as described in [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.3.2.1 paragraph 3. But an array name that's the operand of a unary `&` or `sizeof` operator *is* an lvalue. – Keith Thompson Feb 07 '21 at 04:15
  • We can also note also note that `(x)` is an lvalue if `x` is an lvalue. This would be obvious and pedantic were it not for the fact that the C standard says otherwise in C 2018 6.3.2.1 2: “Except when it is the operand of the `sizeof` 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 (and is no longer an lvalue)…” 6.5.1 5 says it is an lvalue but leaves it to the reader to resolve the conflict with 6.3.2.1 2. – Eric Postpischil Feb 07 '21 at 12:14