when explaining constant expressions, the standard (well, draft N1570) gives thi "enlightening" example:
118)
Thus, in the following initialization,
static int i = 2 || 1 / 0;
the expression is a valid integer constant expression with value one.
How is this expression evaluated?
The logical or, || always returns either 0 or 1. First its left operand is evaluated, then, if the evaluation of the left operand resulted in 0, the right operand is evaluated and the value of the expression is 0 if the right operand also evaluates to 0, 1 otherwise. If the evaluation of the left operand resulted in a nonzero value, the entire expression evaluates to 1 without evaluating the right operand.
The precedence of the division operator / is higher than the precedence of the logical or, the expression
2 || 1 / 0
is implicitly parenthesized
2 || (1 / 0)
As an expression-tree:
(||)
/ \
2 (/)
/ \
1 0
The precedence determines the shape of the tree, but the order of evaluation is independent from the precedence (except insofar as the precedence determines data-dependencies). For some operators (||, &&, ?:, ,), the order of evaluation of the operands is specified [and the right operands of || and && aren't evaluated at all if the result is already determined after evaluation of the left operand, and of the second and third operands of ?:, only one is evaluated - which one is determined by the evaluation of the first operand], generally the order of evaluation of the children of an operator-node is unspecified.
Since the left operand of || in
static int i = 2 || (1 / 0);
(the constant expression 2) evaluates to a nonzero value, the evaluation of the expression stops there and the value of
2 || 1 / 0
is 1.
The evaluation of || is specified in section 6.5.14, paragraph 4:
Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.
and its return value ibid, paragraph 3:
The || operator shall yield 1 if either of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.
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.
() has the highest priority, why is it short-circuited?
int a = 1, b = 0;
(--a)&&(b++);
Why is (b++) still short-circuited?
I don't find the term "short-circuit" particularly helpful.
It is better to say that && and || have left-to-right order of evaluation. The C standard (6.5.13) actually explains this well:
the && operator guarantees left-to-right evaluation ...
If the first operand compares equal to 0, the second
operand is not evaluated.
And that's it. The left operand --a evaluates to 0.
What is the difference between operator precedence and order of evaluation?
In this case, all that matters is order of evaluation. Operator precedence only guarantees that the expression is parsed as expected - which operands that "glue" to which operator.
The expression (--a) uses the prefix operator and evaluates to 0. This is false and the 2nd expression is not evaluated due to short-circuit as you noted.
To understand consider the following expression
a * b + c * d
If to assume that the operands of the operator + are evaluated from left to right then at first the expression a * b will be evaluated and then the expression c * d.
The assumption relative to the order of evaluations of operands of the operator + is not valid. But it is valid for the operator &&.
So left operand of the expression
(--a)&&(b++);
is evaluated first. And if its value is not equal to 0 then the second operand is evaluated.
From the C Standard (6.5.13 Logical AND operator)
4 Unlike the bitwise binary & operator, the && operator guarantees
left-to-right evaluation; if the second operand is evaluated, there
is a sequence point between the evaluations of the first and second
operands. If the first operand compares equal to 0, the second operand
is not evaluated.
You are right that you can override operator precedence using parenthesis ().
For example, the expression
a && b || c
is equivalent to
(a && b) || c,
because && has a higher precedence than ||. You can override this precedence by changing it to
a && (b || c)
so that now the || has precedence over &&.
However, operator procedence doesn't change the behavior of the individual operators. In particular, both operators will still use short-circuiting. So, even if || has higher precedence than &&, the && operator will still evaluate its left-hand operand before its right-hand operand.
In both of my examples above, the && operator will still evalulate a before evaluating b or (b || c). Operator precedence will only affect whether the right-hand operand of the && operator is the expression b or (b || c).
Therefore, in your example, by writing (--a)&&(b++) instead of --a&&b++, you are merely ensuring that the -- and ++ operators have precedence over the && operator. This is not necessary, because he C language specifies that these operators already have precedence.
In the hypothetical scenario that the C language had instead specified that the && operator had precedence over the -- and ++ operators, then the expression --a&&b++ would be interpreted as --(a&&b)++, and it would be necessary to write (--a)&&(b++) to prevent this interpretation. But this is not the case.
I'm using C language, for the below code:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num1=0;
int res = ++num1 && num1++;
printf("%d\t%d\n",num1,res);
}
In the above code I get output as 2 1. I think the output should be 2 0.
Please correct me if wrong, to solve this statement, the num1++(0) would be executed first due to highest precedence and then ++num1(2) would be executed and then at last && will be preformed because it has the lowest precedence.
Please comment how is this statement getting executed.
In some of the tutorials I find that postfix ++ and prefix ++ have the same precedence, but if that is true then according to the associativity rule again num1++ should be executed first(right to left) which should again lead to answer as 2 0.
In the expression used as an initializer
int res = ++num1 && num1++;
there is a sequence point for the operator &&.
From the C Standard (6.5.13 Logical AND operator)
3 The && operator shall yield 1 if both of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
and
4 Unlike the bitwise binary & operator, the && operator guarantees
left-to-right evaluation; if the second operand is evaluated, there is
a sequence point between the evaluations of the first and second
operands. If the first operand compares equal to 0, the second operand
is not evaluated.
At first the left operand of the operator is evaluated and as a result num1 will be equal to 1 due to the unary (prefix) increment operator. As the sub-expression is not equal to 0 then the second operand is evaluated. Its value is the value before incrementing that is 1. As this second operand is also unequal to 0 then the whole expression is evaluated to the logical true and its value is 1 (see the first quote from the C Standard).
This value 1 is assigned to the variable res while the variable num1 after the postfix increment will be equal to 2.
So you will have that after this declaration res is equal to 1 and num1 is equal to 2.
Lots of misconceptions here. First of all, operator precedence states the order of parsing, not the order of execution. There are two related but different terms, operator precedence and order of evaluation.
See What is the difference between operator precedence and order of evaluation?.
Once you understand order of evaluation, the && operator specifically comes with well-defined sequencing, which isn't normally the case for C operators. It guarantees a left-to-right order of evaluation. C17 6.5.14/4:
Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the
second operand is evaluated, there is a sequence point between the evaluations of the first
and second operands. If the first operand compares unequal to 0, the second operand is
not evaluated.
Normally, you wouldn't be able to wild and crazy things with the ++ operator mixed with other operators, but the above && rule makes it possible in this specific case.
See Why can't we mix increment operators like i++ with other operators? It explains sequencing/sequence points.
In some of the tutorials I find that postfix ++ and prefix ++ have the same precedence,
They don't, prefix ++ takes precedence over postfix (and other unary operators). So associativity does not apply.
Please correct me if wrong, to solve this statement, the num1++(0) would be executed first due to highest precedence and then ++num1(2) would be executed and then at last && will be preformed because it has the lowest precedence.
Precedence only controls which operators are grouped with which operands - it does not affect the order in which expressions are evaluated.
The &&, ||, ?:, and comma operator all force left-to-right evaluation - the left operand is fully evaluated (and any side effects applied) before the right operand. && and || both short circuit - for &&, the right operand will be evaluated only if the left operand is non-zero.
The unary (prefix) ++ operator yields the current value of the operand plus 1, so the result of ++num1 is 1. As a side effect the value in num1 is incremented. Since this result is non-zero, num1++ is also evaluated. The postfix ++ operator yields the current value of the operand, so the result of num1++ is 1. As a side effect the value in num1 is incremented.
The result of an && expression is 1 if both operands are non-zero, 0 otherwise.
It's roughly equivalent to writing
tmp = num1 + 1;
num1 = num1 + 1;
res = 0;
if ( tmp != 0 )
{
if ( num1 != 0 )
{
res = 1;
}
}
num1 = num1 + 1;
So the result of ++num1 && num1++ is 1, and the value stored in num1 at the end of it is 2.
In some of the tutorials I find that postfix ++ and prefix ++ have the same precedence,
That is very wrong and you should stop using those tutorials immediately. Postfix operators have higher precedence than unary operators - *a++ is parsed as *(a++), ++a[i] is parsed as ++(a[i]), etc. An expression like ++i++ would be parsed as ++(i++), but you can't write such an expression in C - the result of i++ isn't an lvalue and cannot be the operand of a unary ++ like that.
In the following code, I am getting 10 | 1 | 1 as a result. But according to precedence rules shouldn't 'and' operator must be evaluated first?(and yield c=9) Like : d = a || (--b)&&(--c) since 'and' has higher precedence. ( or shortcutting breaks precedence rules ?)Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
int main(){
int a,b,c,d;
a =1;
b = c = 10;
d = a|| --b&&--c;
printf("%d\n",c);
printf("%d\n",a);
printf("%d\n",d);
return 0;
}
Precedence and order of evaluation are two different things. From Logical OR
documentation (emphasis mine):
There is a sequence point after the evaluation of lhs. If the result of lhs compares unequal to zero, then rhs is not evaluated at all (so-called short-circuit evaluation).
In case of exp1 || exp2, exp1 is always evaluated first as there is a sequence point after it and if exp1 is non-zero then exp2 is not evaluated.
Precedence only determines which operands are grouped with which operators - it does not control the order in which expressions are evaluated.
In your example, it means the expression is parsed as
a || (––b && ––c)
Both || and && force left-to-right evaluation1. Both introduce a sequence point (IOW, the left hand operand will be evaluated and all side effects will be applied before the right hand operand is evaluated).
Both operators short-circuit - if the left operand of || evaluates to non-zero, then the result of the expression is 1 (true) regardless of the value of the right operand, so the right operand isn’t evaluated at all. If the left operand of && is 0, then the result of the expression is 0 (false) regardless of the value of the right operand, so the right operand isn’t evaluated at all.
In your expression, a is evaluated first. It has a non-zero value (1), so ––b && ––c is not evaluated.
Along with the ?: and comma operators. All other operators (arithmetic, equality, subscript, etc.) do not force a particular order of evaluation.
This post-increment operator usage is confusing.
{int a=0,b=1,c=2,d;
d=a++||b++||c++
printf("%d %d %d %d",a,b,c,d);}
output is
1,2,2,1
value of c did not increase but if I replace it with && operator it increases. Why?
Quoting C11, chapter §6.5.14, (emphasis mine)
Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the
second operand is evaluated, there is a sequence point between the evaluations of the first
and second operands. If the first operand compares unequal to 0, the second operand is
not evaluated.
So, in your case,
d=a++||b++||c++
is the same as
d= ( (a++ || b++) || c++)
Then, the statement inside the first parenthesis is evaluated, first a++ (post-increment) evaluates to 0 (side-effect pending), so the RHS of the first || is evaluated, b++, producing 1 and the result of the || operation is TRUE, yields 1.
That result, 1, is the LHS of the second ||. Hence, the RHS of the second || (c++) is not evaluated anymore and the final result becomes TRUE, again yielding 1, which gets stored in d.
So, finally,
a++ is evaluated, becomes 1
b++ is evaluated, becomes 2
c++ is not evaluated, remains 2
the result of || is stored in d, that is TRUE, so stores 1.
On the other hand, for logical AND && operator,
[...] If the first operand compares equal to 0, the second
operand is not evaluated.
So, if you replace the last || with &&, then for the outer statement, the LHS becomes 1 and the RHS evaluates, making c++ to be evaluated and incremented, as a side effect.