Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anyone explain left value and right value in Assembly language level?

Tags:

c++

c

lvalue

I think everyone here knows that --i is a left value expression while i-- is a right value expression. But I read the Assembly code of the two expression and find out that they are compiled to the same Assembly code:

mov eax,dword ptr [i]
sub eax,1
mov dword ptr [i],eax

In C99 language standard, An lvalue is defined to an expression with an object type or an incomplete type other than void.

So I can ensure that --i return a value which is an type other than void while i-- return a value which is void or maybe a temp variable.

However when I give a assignment such as i--=5, the compiler will give me an error indicating i-- is not a lvalue, I do no know why it is not and why the return value is a temp variable. How does the compiler make such a judgement? Can anybody give me some explanation in Assembly language level?Thanks!

like image 562
jaurung Avatar asked Jan 14 '23 16:01

jaurung


2 Answers

Left value? Right value?

If you are talking about lvalues and rvalues, then the property of being lvalue or rvalue applies to the result of an expression, meaning that you have to consider the results of --i and i--. And in C language both --i and i-- are rvalues. So, your question is based on incorrect premise in the realm of C language. --i is not an lvalue in C. I don't know what point you are trying to make by referring to the C99 standard, since it clearly states that neither is an lvalue. Also, it is not clear what you mean by i-- returning a void. No, the built-in postfix -- never returns void.

The lvalue vs. rvalue distinction in case of --i and i-- exists in C++ only.

Anyway, if you are looking at mere --i; and i--; expression statements, you are not using the results of these expressions. You are discarding them. The only point to use standalone --i and i-- is their side-effects (decrement of i). But since their side-effects are identical, it is completely expected that the generated code is the same.

If you want to see the difference between --i and i-- expressions, you have to use their results. For example

int a = --i;
int b = i--;

will generate different code for each initialization.

This example has nothing to do with lvalueness or rvalueness of their results though. If you want to observe the difference from that side (which only exists in C++, as I said above), you can try this

int *a = &--i;
int *b = &i--;

The first initialization will compile in C++ (since the result is an lvalue) while the second won't compile (since the result is an rvalue and you cannot apply the built-in unary & to an rvalue).

The rationale behind this specification is rather obvious. Since the --i evaluates to the new value of i, it is perfectly possible to make this operator to return a reference to i itself as its result (and C++ language, as opposed to C, prefers to return lvalues whenever possible). Meanwhile, i-- is required to return the old value of i. Since by the time we get to analyze the result oh i-- the i itself is likely to hold the new value, we cannot return a reference to i. We have to save (or recreate) the old value of i in some auxiliary temporary location and return it as the result of i--. That temporary value is just a value, not an object. It does not need to reside in memory, which is why it cannot be an lvalue.

like image 72
AnT Avatar answered Jan 22 '23 01:01

AnT


[Note: I'm answering this from a C++ perspective.]

Assuming i is a built-in type, if you just write --i; or i--; rather than, say, j = ++i; or j = i++;, then it's unsurprising that they get compiled to the assembly code by the compiler - they're doing the same thing, which is decrementing i. The difference only becomes apparent at the assembly level when you do something with the result, otherwise they effectively have the same semantics.

(Note that if we were thinking about overloaded pre- and post-decrement operators for a user-defined type, the code generated would not be the same.)

When you write something like i-- = 5;, the compiler quite rightly complains, because the semantics of post-decrement are essentially to decrement the thing in question but return the old value of it for further use. The thing returned will be a temporary, hence why i-- yields an r-value.

like image 20
Stuart Golodetz Avatar answered Jan 22 '23 00:01

Stuart Golodetz