Consider the following C code:
#include <stdio.h>
int main(void) {
int a = 0;
printf("%d\n", ++a += 1);
}
++ has precedence over += so it should be evaluated first. So we have a on the left and 1 on the right. Now based on my understanding ++a should result in a (only changes its value) which is lvalue so why this statement gives the following error:
lvalue required as left operand of assignment
The prefix increment operator ++ results in the incremented value of its operand, however it is not an lvalue. Section 6.5.3.1p2 of the C standard: describes the semantics as follows:
The value of the operand of the prefix ++ operator is incremented.
The result is the new value of the operand after incrementation. The
expression ++E is equivalent to (E+=1). See the discussions of
additive operators and compound assignment for information on
constraints, types, side effects, and conversions and the effects of
operations on pointers.
Then section 6.5.16.2p3 regarding compound assignment operators states:
A compound assignment of the form E1 op = E2 is equivalent to the
simple assignment expression E1 = E1 op (E2), except that the lvalue
E1 is evaluated only once, and with respect to an
indeterminately-sequenced function call, the operation of a compound
assignment is a single evaluation.
And 6.5.16p3 regarding the assignment operator further states:
An assignment operator stores a value in the object designated by the
left operand. An assignment expression has the value of the left
operand after the assignment, but is not an lvalue.
So it is explicitly not allowed. Even if it was, an expression such as ++a += 1 would cause a to be modified more than once without an intervening sequence point which would trigger undefined behavior.
This is one of those place where C and C++ differ. C++ does in fact allow the result of the = operator, and by extension compound assingment and prefix ++/--, to be an lvalue.
Related
We know that the conditional operator returns an lvalue if its Expr2 and Expr3 are lvalues of the same type otherwise it returns an rvalue:
int main(){
int a = 0, b = 0;
(a == 1 ? a : b) = 1; // C++ OK. C: error. lvalue needed
return 0;
}
The code above compiles fines on C++ while it fails to compile on C.
Here is the output on C:
main.c: In function ‘main’:
main.c:8:20: error: lvalue required as left operand of assignment
(a == 1 ? a : b) = 1;
^
In C, the result1 of the conditional operator is an rvalue (a “value” in the terminology of the C standard) because C 2018 6.3.2.1 2 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);…
Thus, while the result of the conditional operator is its second or third operand, that operand has been converted to a value and is no longer an lvalue.
As M.M notes, you can get the same effect as selecting an lvalue by selecting addresses of operands instead and then applying *:
*(a == 1 ? &a : &b) = 1;
Footnote
1 The result of the conditional operator is not a return value, as there is no function call involved, so there is no call-and-return, just an expression evaluation.
In this C11 Draft Standard, there is a footnote that makes it clear that the result of a conditional operator is not an lvalue:
6.5.15 Conditional operator
…
Semantics
4 The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of
the second or third operand (whichever is evaluated). The second
operand is evaluated only if the first compares unequal to 0; the
third operand is evaluated only if the first compares equal to 0; the
result is the value of the second or third operand (whichever is
evaluated), converted to the type described below.110)
110) A conditional expression does not yield an lvalue.
Furthermore, in this later draft Standard (dated March, 2019), the wording hasn't changed significantly (except that the footnote is therein numbered 116, rather than 110).
This is one of those cases where C++ and C differ. In C the conditional operator results in an rvalue and therefore it cannot be the left side of an assignment.
expression1? expression2 : expression3
If expression 1 has a true (non-zero) value, then the entire conditional expression takes the same value as expression 2 . If expression 1 has a false (null) value, then the entire conditional expression gets the same value as expression 3.
P r i m e r s
(5 > 3) ? 1 : 2 gets the value 1.
(3 > 5) ? 1 ; 2 gets the value 2.
(a > b) ? a : b gets a larger value among a or b.
The conditional operation itself is not a function that
returns a value.
My question is about the following line of code, taken from "The C Programming Language" 2nd Edition:
*p++->str;
The book says that this line of code increments p after accessing whatever str points to.
My understanding is as follows:
Precedence and associativity say that the order in which the operators will be evaluated is
->
++
*
The postfix increment operator ++ yields a value (i.e. value of its operand), and has the side effect of incrementing this operand before the next sequence point (i.e. the following ;)
Precedence and associativity describe the order in which operators are evaluated and not the order in which the operands of the operators are evaluated.
My Question:
My question is around the evaluation of the highest precedence operator (->) in this expression. I believe that to evaluate this operator means to evaluate both of the operands, and then apply the operator.
From the perspective of the -> operator, is the left operand p or p++? I understand that both return the same value.
However, if the first option is correct, I would ask "how is it possible for the evaluation of the -> operator to ignore the presence of the ++".
If the second option is correct, I would ask "doesn't the evaluation of -> in this case then require the evaluation of a lower precedence operator ++ here (and the evaluation of ++ completes before that of ->)"?
To understand the expression *p++->str you need to understand how *p++ works, or in general how postfix increment works on pointers.
In case of *p++, the value at the location p points to is dereferenced before the increment of the pointer p.
n1570 - §6.5.2.4/2:
The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). [...]. The value computation of the result is sequenced before the side effect of updating the stored value of the operand.
In case of *p++->str, ++ and -> have equal precedence and higher than * operator. This expression will be parenthesised as *((p++)->str) as per the operator precedence and associativity rule.
One important note here is precedence and associativity has nothing to do with the order of evaluation. So, though ++ has higher precedence it is not guaranteed that p++ will be evaluated first. Which means the expression p++ (in the expression *p++->str) will be evaluated as per the rule quoted above from the standard. (p++)->str will access the str member p points to and then it's value is dereferenced and then the value of p is incremented any time between the last and next sequence point.
Postfix ++ and -> have the same precedence. a++->b parses as (a++)->b, i.e. ++ is done first.
*p++->str; executes as follows:
The expression parses as *((p++)->str). -> is a meta-postfix operator, i.e. ->foo is a postfix operator for all identifiers foo. Postfix operators have the highest precedence, followed by prefix operators (such as *). Associativity doesn't really apply: There is only one operand and only one way to "associate" it with a given operator.
p++ is evaluated. This yields the (old) value of p and schedules an update, setting p to p+1, which will happen at some point before the next sequence point. Call the result of this expression tmp0.
tmp0->str is evaluated. This is equivalent to (*tmp0).str: It dereferences tmp0, which must be a pointer to a struct or union, and gets the str member. Call the result of this expression tmp1.
*tmp1 is evaluated. This dereferences tmp1, which must be a pointer (to a complete type). Call the result of this expression tmp2.
tmp2 is ignored (the expression is in void context). We reach ; and p must have been incremented before this point.
running this code:
#include <stdio.h>
int main() {
int x[]={20,30};
int *p=x;
++*p++;
printf("%d %d\n",x[0],*p);
return 0;
}
the output is 21 30 which is something that doesn't make sense to me because according to C operator precedence the postfix increment comes first though if that was the case in my opinion the output should be 20 31.For the record i am new to programming and it really seems that i cant get the hang of it so sorry if this question is stupid :)
From the C++ Standard (the same is valid for the C Standard)
5.2 Postfix expressions
1 Postfix expressions group left-to-right.
Postfix expressions and p++ is a postfix expression have higher priority than unary expressions.
The C++ Standard
5.3 Unary expressions
1 Expressions with unary operators group right-to-left.
In this expression ++*p there are two unary subexpressions: *p and ++( *p )
So the whole expression can be written like
++( *( p++ ) );
Take into account regarding the postfix expression ++ that (now it is the C Standard)
6.5.2.4 Postfix increment and decrement operators
2 The result of the postfix ++ operator is the value of the operand.
As a side effect, the value of the operand object is incremented (that
is, the value 1 of the appropriate type is added to it).
Let's consider the result of the expression statement
++( *( p++ ) );
subexpression p++ has the value of its operand that is the address of type int * of the first element of the array. Then due to the dereferencing the expression *( p++ ) yields the lvalue of the first element of the array that is x[0] and then its value is increased. So the first element of the arry now has the value 21.
At the same time the postfix increment incremented the pointer p as its side effect (see the quote above from the C Standard). Its now points to the second element of the array.
Thus the output will be
21 30
You first increment where p points to and then you advance the pointer by one.
So, p points to 20, thus ++20 = 21.
Then the pointer will be increased by once, and due to pointer's arithmetic, it will point to next element of 20, which is 30, in your array.
As M.M said, you are confusing, the order of evaluation with precedence. Read more about it here.
according to C operator precedence the postfix increment comes first
Precedence is not the same thing as order of evaluation.
Precedence controls which operators are grouped with which operands. In this case, the expression ++*p++; is parsed as ++(*(p++)).
The order of evaluation is
Evaluate p++; the result of this evaluation is &x[0], and the side effect is to advance p to point to x[1];
Dereference the result of 1; the result of this evaluation is x[0];
Apply the prefix ++ operator to the result of 2; the result of this evaluation is x[0] + 1, with the side effect that the value stored in x[0] is incremented.
Remember that side effects do not have to be applied immediately upon evaluation; they may be deferred until a sequence point.
#include<stdio.h>
int main()
{
int i = 10;
printf("%d", ++(-i));
return 0;
}
This is obviously an Compilation Error as post(pre)increment/decrement take l-values. So in this case -i is r-value BUT how and why?
The preincrement operator ++ (and other similar operators) requires an lvalue, which is an expression that not only has a type/value but also refers to an object. Roughly speaking, lvalues are things you could put on the left hand side of the = operator (but there are some exceptions) or put after the & (address-of) operator (but there are some exceptions here too).
The term rvalue is slang (not defined by the language standard) for expressions which are not lvalues. In the case of -i, i is an object, but there is no object -i; -i is just the resulting value from negating the value of i. Assigning something to -i is not possible because there is no object/storage it refers to.
The unary - operator produces a r-value. ++ cannot operate on r-value. Very right.
Now, to answer the "why", let me quote C11, chapter §6.5.3.3
The result of the unary - operator is the negative of its (promoted) operand.
The "result" here, is a computed value, it is not held by any variable. In other words, as mentioned in the comments, -i does not produce any object, so it is not an lvalue, it is considered as r-value, or non-lvalue.
Will right side always evaluated to ahead of left side? And then the result of right side will be passed on to left side. I am not talking about the exception such as A[i]=i++
I am talking about the normal cases:
A[i] = (j+32+43 & K);
A[j] != (A[j] + A[k]);
will the right part of all these expression evaluated first and then the result is compared to the left side? (Always)
In general the order of evaluation of sub-expressions is unspecified, there are a few exceptions such as logical and, logical or, comma operator, etc...
Since you comment stated you are interested in the general rule:
any operstor #YuHao if there is any general rule
that would be covered by the draft C99 standard section 6.5 Expressions paragraph 3 which says (emphasis mine going forward):
The grouping of operators and operands is indicated by the syntax.74)
Except as specified later (for the function-call (), &&, ||, ?:, and
comma operators), the order of evaluation of subexpressions and the
order in which side effects take place are both unspecified.
this is basically the same in the draft C11 standard expect C11 does not list the exceptions so quoting C99 is more convenient. Paragraph 3 in C11 says:
The grouping of operators and operands is indicated by the syntax.85)
Except as specified later, side effects and value computations of
subexpressions are unsequenced.86)
Specifically for assignment operators C99 says:
The order of evaluation of the operands is unspecified [...]
and C11 says:
[...] The evaluations of the operands are unsequenced.
No, there is no such guarantee, N1570 §6.5.16/p3 (emphasis mine):
An assignment operator stores a value in the object designated by the
left operand. An assignment expression has the value of the left
operand after the assignment,111) but is not an lvalue. The type of an
assignment expression is the type the left operand would have after
lvalue conversion. The side effect of updating the stored value of the
left operand is sequenced after the value computations of the left and
right operands. The evaluations of the operands are unsequenced.
Note that assignment operator "consumes" two operands and has side-effect of modifying a lvalue.
I just encountered this today, and spent 1 hour debugging code like this:
int a[1], b=0;
a[b++] = b;
I expected a[0] to contain 0 after this, but the compiler actually decided to evaluate b++ first, then right side of assignment, and store result in a[0] (so b++ on the left side worked as it should). So this effectively became:
b++;
a[0] = b; // 1
This will depend on the precedence and associativity of the operators involved.
A full list can be found here