rvalue: static, read only value, generally a number. Example: 2, (2+2), (i+2), i++. Similar to 'const' in C++.
lvalue: Can be assigned to but also has a value, generally a variable or an expression that returns a variable. Examples: i, (++i), (i += 3), etc.
The distinction comes in when you look at the type of a value in an expression, for example:
a = 2 + 3;
In this example, a is an lvalue, 2 and 3 are rvalues, and the sum 5 is also a (hidden) rvalue.
a = ++i;
Here, a is an lvalue because it's being assigned to, and i is an lvalue because it is a variable, but critically, (++i) is also an lvalue because the operator '++' returns the variable it's operating on after the incrementation operation, rather than just the resulting value. This allows expressions like ++++i to compile and have meaning, because they're effectively (++(++i)).
This begs the question: what does a = (++i = i) do? No idea.
Why is this more important to C++ programmers than C programmers? C programmers have to deal with this rvalue/lvalue distinction only if they're writing expressions that have multiple assignments in them, which you generally shouldn't do because it's confusing to read and understand. C++ programmers have the same problems, but they also have to understand the rules when they use an advanced C++ feature called operator overloading. This allows objects to respond to operators like '++' with behaviors specific to that object. For example, an iterator could move to the next object in the list when ++ is called.
However, there's a big difference between the preincrement form '++i' and the postincrement form 'i++'. Preincrement returns the lvalue variable i after it performs the 'increment' operation, and postincrement returns the lvalue variable i before it performs the operation. When a C++ programmer implements postincrement, they have to copy the object that is being incremented, and return the old version after incrementing the new one.