Consider the following code snippet
int a,i;
a = 5;
(i++) = a;
(++i) = a;
cout<<i<<endl;
Line (++i) = a is compiling properly and giving 5 as output.
But (i++) = a is giving compilation error error: lvalue required as left operand of assignment.
I am not able to find the reason for such indifferent behavior. I would be grateful if someone explains this.
The expression i++ evaluates to the value of i prior to the increment operation. That value is a temporary (which is an rvalue) and you cannot assign to it.
++i works because that expression evaluates to i after it has been incremented, and i can be assigned to (it's an lvalue).
More on lvalues and rvalues on Wikipedia.
According to the C++ standard, prefix ++ is an lvalue (which
is different than C), post-fix no. More generally, C++ takes
the point of view that anything which changes an lvalue
parameter, and has as its value the value of that parameter,
results in an lvalue. So ++ i is an lvalue (since the
resulting value is the new value of i), but i ++ is not
(since the resulting value is not the new value, but the old).
All of this, of course, for the built-in ++ operators. If you
overload, it depends on the signatures of your overloads (but
a correctly designed overloaded ++ will behave like the
built-in ones).
Of course, neither (++ i) = a; nor (i ++) = a; in your
example are legal; both use the value of an uninitialized
variable (i), which is undefined behavior, and both modify i
twice without an intervening sequence point.
Related
I have been trying to learn the associativity of operators in C++ and I have come across a code segment :
int a = 10;
int C = a++ + ++a + ++a +a;
I have also studied that ++a is right to left associative and a++ is left to right associative. Also + is left to right associative. But I don't understand how to apply this knowledge in this problem.
I am confused that how this statement will be parsed by my compiler?
I am also puzzled that since putting spaces don't matter much why does removing spaces like :
int C = a+++++a+++a+a; //error: lvalue required as increment operand
generate an error?
Please help me understand this concept.
Thanks!
First of all space does matter- It helps compiler to resolve ambiguity.
Whenever there is an expression, compiler parse it from right to left. It looks for all the post increment operators first and then pre increment operators as later has lower precedence than the former. So any modification done by pre-increment operator will be applied to the whole expression and then changes of post-increment will be applied in the next expression.
Explanation
++a first increments the value of a and then returns lvalue referring to a, so if a is used then it will be the incremented value.
In your case there are total two ++a, thus the value of a will be incremented to 12 and thus assigned to a. so all the a in your expression will be holding the value 12 giving you the value of c=48.
a++ first returns an rvalue whose value is a, that is the old value, and then increments a at an unspecified time before the next full expression.
In your case if you use value of a after the expression it will be 13 as in the previous expression there was only one a++.
For eg.
int a = 10;
int C = a++ + ++a + ++a +a; // Here a=12 and the post increment effect will be applied in the next expression
int B = a + a; // Here a=13 the effect of previous post increment.
Regarding Error
With no space in expression, compiler will get confused when it will parse expression and thus dosent have any value to do the assignment.
PS: lvalue is a value that can be the target of an assignment.
In C/C++ the pre-increment (decrement) and the post-increment (decrement) operators require an L-value expression as operand. Providing an R-value or a const qualified variable results in compilation error.
Putting aside the fact that it would result in UB (as no sequence points between these multiple increments of the same variable)
a+++++a+++a+a
is parsed (as parser is greedy) as
((a++)++) + (a++) + a + a
and (a++)++ is illegal when a is a built-in type as int.
test.(c/cpp)
#include <stdio.h>
int main(int argc, char** argv)
{
int a = 0, b = 0;
printf("a = %d, b = %d\n", a, b);
b = (++a)--;
printf("a = %d, b = %d\n", a, b);
return 0;
}
If I save the above as a .cpp file, it compiles and outputs this upon execution:
a = 0, b = 0
a = 0, b = 1
However, if I save it as a .c file, I get the following error:
test.c:7:12: error: lvalue required as decrement operator.
Shouldn't the (++a) operation be resolved before the (newValue)-- operation? Does anyone have any insight on this?
In C the result of the prefix and postfix increment/decrement operators is not an lvalue.
In C++ the result of the postfix increment/decrement operator is also not an lvalue but the result of the prefix increment/decrement operator is an lvalue.
Now doing something like (++a)-- in C++ is undefined behavior because you are modifying an object value twice between two sequence points.
EDIT: following up on #bames53 comment. It is undefined behavior in C++98/C++03 but the changes in C++11 on the idea of sequence points now makes this expression defined.
In C and C++, there are lvalue expressions which may be used on the left-hand side of the = operator and rvalue expressions which may not. C++ allows more things to be lvalues because it supports reference semantics.
++ a = 3; /* makes sense in C++ but not in C. */
The increment and decrement operators are similar to assignment, since they modify their argument.
In C++03, (++a)-- would cause undefined behavior because two operations which are not sequenced with respect to each other are modifying the same variable. (Even though one is "pre" and one is "post", they are unsequenced because there is no ,, &&, ?, or such.)
In C++11, the expression now does what you would expect. But C11 does not change any such rules, it's a syntax error.
For anybody who might want the precise details of the differences as they're stated in the standards, C99, §6.5.3/2 says:
The value of the operand of the prefix ++ operator is incremented. The result is the new
value of the operand after incrementation.
By contrast, C++11, §5.3.2/1 says:
The result is the updated operand; it is an lvalue, and it is a bit-field if
the operand is a bit-field.
[emphasis added, in both cases]
Also note that although (++a)-- gives undefined behavior (at least in C++03) when a is an int, if a is some user-defined type, so you're using your own overloads of ++ and --, the behavior will be defined -- in such a case, you're getting the equivalent of:
a.operator++().operator--(0);
Since each operator results in a function call (which can't overlap) you actually do have sequence points to force defined behavior (note that I'm not recommending its use, only noting that the behavior is actually defined in this case).
§5.2.7 Increment and decrement:
The value of a postfix ++ expression is the value of its operand. [ ... ] The operand shall be a modifiable lvalue.
The error you get in your C compilation helps to suggest that this is only a feature present in C++.
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!
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.
[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.
The terms “lvalue” and “rvalue” originate from the assignment expression E1 = E2, in which the left operand E1 is used to identify the object to be modified, and the right operand E2 identifies the value to be used. (See C 1999 6.3.2.1, note 53.)
Thus, an expression which still has some object associated with it can be used to locate that object and to write to it. This is an lvalue. If an expression is not an lvalue, it might be called an rvalue.
For example, if you have i, the name of some object, it is an lvalue, because we can find where i is, and we can assign to it, as in i = 3.
On the other hand, if we have the expression i+1, then we have taken the value of i and added 1, and we now have a value, but it is not associated with a particular object. This new value is not in i. It is just a temporary value and does not have a particular location. (To be sure, the compiler must put it somewhere, unless optimization removes the expression completely. But it might be in registers and never in memory. Even if it is in memory for some reason, the C language does not provide you for a way to find out where.) So i+1 is not an lvalue, because you cannot use it on the left side of an assignment.
--i and i++ are both expressions that result from taking the value of i and performing some arithmetic. (These expressions also change i, but that is a side effect of the operator, not part of the result it returns.) The “left” and “right” of lvalues and rvalues have nothing to do with whether -- or ++ operator is on the left side or the right side of a name; they have to do with the left side or the right side of an assignment. As other answers explain, in C++, when they are on the left side of an lvalue, they return an lvalue. However, this is coincidental; this definition of the operators in C++ came many years after the creation of the term “lvalue”.
In C++, pre-increment operator gives lvalue because incremented object itself is returned, not a copy.
But in C, it gives rvalue. Why?
C doesn't have references. In C++ ++i returns a reference to i (lvalue) whereas in C it returns a copy(incremented).
C99 6.5.3.1/2
The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation. The expression ++Eis equivalent to (E+=1).
‘‘value of an expression’’ <=> rvalue
However for historical reasons I think "references not being part of C" could be a possible reason.
C99 says in the footnote (of section $6.3.2.1),
The name ‘‘lvalue’’ comes originally
from the assignment expression E1 =
E2, in which the left operand E1 is
required to be a (modifiable) lvalue.
It is perhaps better considered as
representing an object ‘‘locator
value’’. What is sometimes called
‘‘rvalue’’ is in this International
Standard described as the ‘‘value of
an expression’’.
Hope that explains why ++i in C, returns rvalue.
As for C++, I would say it depends on the object being incremented. If the object's type is some user-defined type, then it may always return lvalue. That means, you can always write i++++++++ or ++++++i if type of i is Index as defined here:
Undefined behavior and sequence points reloaded
Off the top of my head, I can't imagine any useful statements that could result from using a pre-incremented variable as an lvalue. In C++, due to the existence of operator overloading, I can. Do you have a specific example of something that you're prevented from doing in C, due to this restriction?
How are the below valid and invalid as shown and what do they mean. When would such a situation arise to write this piece of code.
++x = 5; // legal
--x = 5; // legal
x++ = 5; // illegal
x-- = 5; // illegal
The postfix (x++/x--) operators do not return an lvalue (a value you can assign into).
They return a temporary value which is a copy of the value of the variable before the change
The value is an rvalue, so you could write:
y = x++ and get the old value of x
Given that both operator=() and operator++() can be overloaded, it is impossible to say what the code does without knowing more about the type of thing the operators are being applied to.
Those all modify the value of x more than once between sequence points, and are therefore undefined behavior, which you should carefully avoid. I don't see where the distinction between "legal" and "illegal" comes in - since the behavior is legal, any behavior (including sending assorted email to the Secretary of State) is perfectly in accordance with the Standard.
Assuming that the question is about built-in ++ and -- operators, none of these statements are strictly legal.
The first two are well-formed, i.e. they merely compilable because the result of prefix increment is lvalue. The last two are ill-formed, since the result of postfix increment is not a rvalue, which is why you can't assign to it.
However, even the first two are not legal in a sense that they produce undefined behavior. It is illegal to modify the same object more than once without an intervening sequence point. (Note also, that compilers are allowed to refuse to compile well-formed code that produces undefined behavior, meaning that even the first pair might prove to be non-compilable).
++x and --x both give you back x (after it's been incremented/decremented). At that point you can do what you want with it, including assign it a new value.
x++ and x-- both give you back what x was (just before it was incremented/decremented). Altering this value makes no more sense than changing any ordinary function's return value:
obj->getValue() += 3; // pointless
Frankly, you should never write that. Postincrement and pre-increment (and decrements) should only ever be used on their own. They're just recipes for confusion.
The only place I can think of where such a situation would occur is with an operator overload of operator++ and operator=. Even then, the definition isn't clear. What you code is saying basically is add one to x, then assign 5 to it. A question would arise such as why would you need to increment x before assigning 5 to it? The only possible explanation is if you have some sort of class where the ++ operator somehow increment an internal counter, then the assignment. No idea why such a thing is needed though.