Osman Khalil
Osman Khalil

Reputation: 11

How does C compiler determine a valid lvalue?

I'm trying to figure out how C determines if an expression is a valid LVALUE.

I know declaring variable gives it a named memory space, which is variable name. The variable name can be RVALUE or LVALUE. If used to represent a value its content is used, but if it is used as LVALUE its address is used to tell that the expression at right side is stored in this address. The picture I see for this operation is like ADDRESS=VALUE: That's how the right and left expressions for assignment operator are evaluated.

So why I can't define a variable like int a;, and then use the address of operator to store value in that address, like &a = 5;?

I know &a returns a constant pointer, but that means I can't change the address or I can't change the value stored in the address? If its content can't be changed, then why using *&a=5 works?

Why I can't assign a value this way, although the left hand expression is always evaluated to an address as I understand? Maybe something is wrong in my understanding?

Upvotes: 1

Views: 547

Answers (2)

First of all, C does not use the term rvalue, preferring the term "value of an expression". The term lvalue is used, and it means (C11 6.3.2.1p1)

[...] an expression (with an object type other than void) that potentially designates an object)

It does not mean the address of the object, it means that the lvalue is the object.

The operand of & more often than not is an lvalue too

  1. The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

The result is a value of an expression of a pointer type, an address. Even though an address points to an object, it is not the object. Just like 1600 Pennsylvania Avenue NW in Washington, D.C. is an address, but it is not the building found at that address.

So if you have a house:

house my_House;

you can ask for its address

&my_house;

which is the address of your house, but it is not a house, i.e. not an lvalue, but the house located at the address of your house is a house, i.e. an lvalue:

*&my_house;

Upvotes: 0

Eric Postpischil
Eric Postpischil

Reputation: 222900

Automatic lvalue conversion

This is covered by C 2018 6.3.2.1 2, which says:

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); this is called lvalue conversion.…

Consider the expression x = y + z:

  • y is an operand of +. The + operator is not in the list of exceptions above. So y is converted to its value.
  • z is an operand of +. The + operator is not in the list of exceptions above. So z is converted to its value.
  • x is the left operand of =, which is the assignment operator. That is in the list of exceptions above. So x remains an lvalue.

About &a = 5

In regard to int a; followed by &a = 5;:

  • The result of the & operator is merely an address—it is just a value; there is no object holding this value, so it is not an lvalue.
  • The assignment operator must have an lvalue as its left operand. C 2018 6.5.16 2 is a constraint that says “An assignment operator shall have a modifiable lvalue as its left operand.”

Therefore &a = 5; violates a constraint, and a C compiler is required to produce a diagnostic message for it. The = operator cannot have a plain value as its left operand.

It is possible to design a programming language so that the assignment operator accepts &a = 5; and uses it to store the value on the right in the location given on the left. The BLISS language does this. In BLISS, the name of a variable always provides its address. To get the value, you must prefix the variable with a period (which acts like C’s unary * operator). So you would write z = .x + .y. So the fact that C does not do this is a choice about aesthetics and convenience, not about logical necessity. In C, lvalues are automatically converted to values in most places, and the exceptions are for operators that act on objects instead of values. In BLISS, you must explicitly designate each lvalue-to-value conversion.

About *a = 5

In *&a=5:

  • The * operator produces an lvalue, per C 2018 6.5.3.2 4: “The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object.…”

Thus *&a provides the lvalue that the assignment operator requires.

Upvotes: 2

Related Questions