lvalue and rvalue

I will skip quoting C99as it is quite difficult to understand the excerpt in its original context.

lvalue is short of "left value", it came originally from assignment operation:

a = b;

Here "a" is a so called lvalue, "b" is a rvalue ( right value ).

The definition of lvalue: expression that refers to something that has addressable storage in the executable. the value of the lvaue is the value of the expression.

A modifiable lvalue is a lvalue whose content can be modified ( not an array, not a const, not incomplete type )

The definition of rvalue: value of an expression.

Based on the definition of rvalue, you can infer that all lvalue is a rvalue too.

The difference between lvalue and rvalue is quite clear. The confusinon arises because C99 never gives a clear definition of rvalue and lvalue. Instead, it give definition via examples. In fact, the word "rvalue" appears only once in C99, whereas lvalue appears 75 times (excluding appearence in Index)

The gist: lvalue refering to an expression that has a addressable data storage inside the executable, "addressable" means you can take the address of it. Modifiable lvalue is a lvalue that you can change the content. If an expression is not a lvalue, it is a rvalue. A lvalue automatically is a rvalue.

Following are some examples:

int a[100];  // a is a lvalue, however it is not a modifiable lvalue.
int a;           // a is a lvalue
int *a;         // a is a lvalue, and because "*a" refers to a storage location, "*a" is a lvalue too.
&a;

"&" operator takes the address of its operand, so its operand must be a lvalue, However, the value of expression "&a" will be stored in a CPU register, no memory storage will be allocated for "&a", hence "&a" is a rvalue.

"abced";

this string literal is a lvalue (though nobody will use it as a lvalue, its storage is really read-only). if you dump the executable binary, you will see a storage of this string literal in the .ro section. It has an address too.

all literal other than string literal are rvalue. E.g, "123", 'c'. The compiler will not allocate storage for them. it mostlikely will be compiled into part of the assembly instruction inside the .text section. E.g., a = 3; where 3 will be part of the assembl instruction.

int abc(int, int );

abc(1,3) = 5; // illegal

The returned value from any function is not a lvalue, because the return value will be store on a CPU register, and no memory storage will allocated, and you cannot take the address of a register.

register int a;

This is a special case. C99 specifically states that we can not take the address of register storage-class. hence "a" is a lvalue but we cannot do "&a", however, we can do "a = 3".

int abc(int, int);

int a = (&&abc)(1,3);  // illegal
int a;
int **b = &&a;           // illegal

The value of expression "&a" is always a rvalue. The operand of "&" needs to be an lvalue.

(a+1) = 2; // illegal

Expression "a+1" will be evaluated to something and will be stored in a CPU register, hence no addressable storage, hence "a+1" is a rvalue.

a + 1 = 2; // illegal

1 + a = 2; // illegal

"+" operator has precedence over "=" operator ( which is the operator with lowest precedence; hence "a+1" will be calculated first, and the value of expression "a+1" is a rvalue.

int a;

a++;

++a;

a++ = 5; // illegal

int *a;

a++ = 0x1234; // illegal

++a = 0x1234; // illegal

--a = 0x1234; // illegal

a-- = 0x1234; // illegal

"++" and "--" operator modifies the content, so the operand has to be a lvalue. However, the value of expression "a++" is not referring to a addressable storage. In fact, it stored in a CPU register, hence "a++", "++a", "a--", "--a" are all rvalues;

int *a;

*++a = 3;

"a" is a pointer, hence expression "++a" evaluate to a value of an address, hence "*(++a)" is a lvalue.