My question is maybe very simple, but I'm wondering what does this x+1 means?
Let's see an example:
int main()
{
int x = 2;
x + 1; //1
if ((x - 2) && (x = 7)) { //2 and 3
// do something
}
}
What i know:
That the assignment cannot be evaluated because left side of && will return false, so the conjunction will never be true.
Questions:
How does the memory looks like after operation 1?
Is the value of x changed after x-2 (2)?
I saw in debugger that this doesn't change the value of x, but I'm using a C++ compiler in Visual Studio so it can give another values.
Thanks in advance :)
The code
x+1;
evaluates the expression and then just drops the results. It's legal but a good compiler should issue a warning (IIRC g++ emits something about an expression that would require side effects to be useful).
The code (x - 2) && (x = 7) instead doesn't do anything, because && is "short-circuited" and x-2 is false in a logical context. So the code (x = 7) is not evaluated. && and || evaluate the left side first and the right side is evaluated only if the result cannot be determined from it... for example (1 || foo()) is guaranteed to skip the call to function foo.
Code like
y = (x - 2) * (x = 7);
would instead be undefined behavior because C++ is not required to work through sub-expressions in sequence (except for the comma operator ,, logical AND &&, logical OR|| and the ternary operator ?:) and using and modifying the same value in different parts of an expression (if these parts don't have a prescribed evaluation sequence) is not permitted but the compilers are not required to complain about it. Whatever happens happens, and it's a programmer's fault.
How does the memory looks like after operation 1?
It's the same as before the operation. x - 1 is an expression without side-effects (i.e. it doesn't modify any variable). The statement x - 1; evaluates the expression and discards the result. Any decent compiler will optimize it away.
To increment x by 1 you should use the compound assignment operator +=:
x += 1;
The same can be achieved with the increment operator ++:
x++; // these do the same
++x; // thing in this case
In other contexts, the two different versions of ++ have different meaning, see What is the difference between prefix and postfix operators?.
Is the value of x changed after x-2 (2)?
x - 2 itself doesn't change the value of x. (Again x -= 2 can be used to achieve that.)
However the assignment x = 7 changes the value of x to 7, if the assignment is evaluated (which happens if the left-hand-side of && evaluates to true or non-zero (this is called short-circuit evaluation). In this case the left-hand-side evaluates to zero so x = 7 is not evaluated).
Note that the = operator is different to the equality comparison operator ==:
x == 7 // evaluates to `true` if the value of `x` is equal to `7`, and to `false` otherwise.
It means "perform this calculation and throw away the result".
In your case that means the compiler will probably just remove the code completely (since it obviously has no side effects). But if operator+ had been overloaded and/or user-defined types had been involved, then there could be meaningful side-effects and the code could be meaningfull and would be kept to perform those operations..
x + 1 does not change value of x. The result of x + 1 viz 3 is calculated and the result is then ignored.
For and (&&) sequence of evaluation is left to right. As long as the components of && operator are true, the next component is evaluated.
As stated earlier for x + 1, similarly, x + 2 does not change value of x. In your case, x - 2 results into 0 viz. zero and so the next component is not evaluated.
The basic principles of C language, remain same across all compilers and IDE (in your case Visual Studio) has no effect on the compilation
Related
I know that C/C++ uses the short-circuit evaluation to evaluate the expression of boolean. For example, C/C++ will definitely evaluate the operand a before the operand b in the expression a && b, if a is false, b won't be evaluated.
Besides, I know that things like 5==6 may be totally ignored by the compiler because it is a constant expression, which can be evaluated at compile time.
But I don't know if b && 0 can be optimized by compiler? Can compiler say: OK, the evaluation of 0 is much easier than the evaluation of b, and b hasn't any side effect, so I decide to change b && 0 into 0 && b to evaluate 0 first.
There are two independent problems involved in your question. The first is that when a compiler "sees" that the if condition is always false (due to && 0), it can completely throw away the corresponding branch. Example translation unit:
bool f(int);
int main()
{
if (f(1) && 0)
return 1;
}
With enabled optimizations, there will be very likely no machine code generated for the branch. However, the f(1) expression must be still evaluated at runtime, since the compiler cannot prove that the f(1) call has no observable behavior.
Machine code: https://godbolt.org/z/sEMrfh
On the contrary, if the compiler could prove that f(1) had no observable behavior, it could eliminate its call away. This has nothing to do with the order of evaluation, but with the as-if rule instead. Demo translation unit:
static bool f(int i)
{
int j = i + 1;
return true;
}
int main()
{
if (f(1) && 0)
return 1;
}
Machine code: https://godbolt.org/z/scs3je
The && and || operators guarantee left-to-right evaluation. Evaluation meaning that the compiler has to check the operand for side effects and if any are present, it must execute those. And if the left operand of && evaluates to zero, it will not evaluate the right one.
Consider if(func() && 0) { do_stuff(); }. Even though the && expression can never be true, the function must still be executed. The compiler isn't going to do some strange re-ordering such as 0 && func(), it will rather just replace the whole expression with func();, removing the if and do_stuff() both.
In general, the compiler is specifically not allowed to re-order the evaluation or execution of the operands of && and ||; they have a so-called sequence point between evaluation of the left and the right operand. Which in turn allows code like (ptr=malloc(...)) && (*ptr = x) to be well-defined and not access a null pointer in case malloc fails.
In the following code excerpt from a larger piece of code presented
void func(int* usedNum, int wher) {
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
}
int main(void) {
int a = 11, b = 2;
func(&a, b);
}
a warning is emitted
warning: operation on '* usedNum' may be undefined [-Wsequence-point]
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
Is there a problem with the code?
My source of doubt was this and the part where it says
The sequence points in the logical expressions such as && and || and ternary operator ?: and the comma operator mean that the left hand side operand is evaluated before the right hand side operand. These few operands are the only operands in C++ that introduce sequence points.
tl;dr
For those that find torturing to read through the comments: The initial question was not properly posed and it would be unfair to create misconceptions. My view on the topic had two sides
The ternary operator does not mess up (in an unexpected way) the sequence points (which holds, the two branches are sequenced in every version of C,C++ - see the link provided)
Is x = ++x the problem? As seen in the coliru link, we compile for c++14. There the operation is well defined (references on the comments), but older versions of c++ and c view this as undefined. So why is there a warning?
Answers focus both in C and C++; this is a good link. Lastly the C tag was there initially (my bad) and can't be removed because existing upvoted answers refer to it
When the condition is true, it is the equivalent of saying x = ++x. In C, and versions of C++ prior to C++11, this constitutes a modification and a read of x without an intervening sequence point and therefore is undefined behaviour if the truthy branch is followed. From C++11 onwards, x = ++x is sequenced and well defined.
Edit To clarify some issues from comments.
1) this would be well defined in all C and C++ standards:
x = (++x, x); // RHS evaluates to x after increment
because the expression in the parentheses involves the comma operator, which introduce a sequence point between the evaluation of its operands. So the whole expression on the RHS evaluates to x after an increment. But the code in your question does not involve the comma operator.
2) The ternary operator introduces a sequence point
It is a sequence point between the condition and the two branches. But this doesn't introduce a sequence point between either branch and the assignment.
The warning you are getting is probably due to the fact that you are compiling your code in c++03 mode or older. In C99 and C++03, expression
x = ++x;
invokes undefined behavior. The reason is that between two sequence points an object can't modify more than once.
This rule is changed in C11 and C++11. According to C11, the rule is as follows:
C11:6.5 Expressions:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
When *usedNum + 1 > wher will be true, then
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
would be equivalent to
*usedNum = ++(*usedNum);
and according to new rule this is well defined in C++11 this is because the side effect by pre ++ is sequenced before the side effect by = operator. Read this answer for more detailed explanation.
But the same expression *usedNum = ++(*usedNum); invokes undefined behavior in C11. The reason is that there is no guarantee that side effect by = operator is sequenced after the side effect of pre ++ operator.
Note: In the expression
a = x++ ? x++ : 0;
there is sequence point after the first x++ and hence behavior is well defined. Same is true for
x = (++x, x);
because there is a sequence point between the evaluation of left and right operand and hence side effect is sequenced.
I was wondering whether the access to x in the last if below here is undefined behaviour or not:
int f(int *x)
{
*x = 1;
return 1;
}
int x = 0;
if (f(&x) && x == 1) {
// something
}
It's not undefined behavior as operator && is a sequence point
It is well defined.
Reference - C++03 Standard:
Section 5: Expressions, Para 4:
except where noted [e.g. special rules for && and ||], the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is Unspecified.
While in,
Section 1.9.18
In the evaluation of the following expressions
a && b
a || b
a ? b : c
a , b
using the built-in meaning of the operators in these expressions, there is a sequence point after the evaluation of the first expression (12).
It is defined. C/C++ do lazy evaluation and it is defined that first the left expression will be calculated and checked. If it is true then the right one will be.
No, because && defines an ordering in which the lhs must be computed before the rhs.
There is a defined order also on ||, ?: and ,. There is not on other operands.
In the comparable:
int x = 0;
if (f(&x) & x == 1) {
// something
}
Then it's undefined. Here both the lhs and rhs will be computed and in either order. This non-shortcutting form of logical and is less common because the short-cutting is normally seen as at least beneficial to performance and often vital to correctness.
It is not undefined behavior. The reason depends on two facts, both are sufficient for giving defined behavior
A function call and termination is a sequence point
The '&&' operator is a sequence point
The following is defined behavior too
int f(int *x) {
*x = 1;
return 1;
}
int x = 0;
if (f(&x) & (x == 1)) {
// something
}
However, you don't know whether x == 1 evaluates to true or false, because either the first or the second operand of & can be evaluated first. That's not important for the behavior of this code to be defined, though.
It's not undefined, but it shouldn't compile either, as you're trying to assign a pointer to x (&x) to a reference.
&& will be evaluated from left to right (evaluation will stop, if the left side evaluates false).
Edit: With the change it should compile, but will still be defined (as it doesn't really matter if you use a pointer or reference).
It will pass the address of the local variable x in the caller block as a parameter to f (pointer to int). f will then set the parameter (which is a temporary variable on the stack) to address 1 (this causes no problem) and return 1. Since 1 is true, the if () will move on to evaluate x == 1 which is false, because x in the main block is still 0.
The body of the if block will not be executed.
EDIT
With your new version of the question, the body will be executed, because after f() has returned, x in the calling block is 1.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Safety concerns about short circuit evaluation
What does the standard say about evaluating && expressions - does it guarantee that evaluation of parameters will stop at the first false?
E.g.:
Foo* p;
//....
if ( p && p->f() )
{
//do something
}
is the f() guaranteed not to be called if p == NULL?
Also, is the order of evaluation guaranteed to be the order of appearence in the clause?
Might the optimizer change something like:
int x;
Foo* p;
//...
if ( p->doSomethingReallyExpensive() && x == 3 )
{
//....
}
to a form where it evaluates x==3 first? Or will it always execute the really expensive function first?
I know that on most compilers (probably all) evaluation stops after the first false is encountered, but what does the standard say about it?
What does the standard say about evaluating && expressions - does it guarantee that evaluation of parameters will stop at the first false?
Yes. That is called short-circuiting.
Also, is the order of evaluation guaranteed to be the order of appearence in the clause?
Yes. From left to right. The operand before which the expression short-circuited doesn't get evaluated.
int a = 0;
int b = 10;
if ( a != 0 && (b=100)) {}
cout << b << endl; //prints 10, not 100
In fact, the above two points are the keypoint in my solution here:
Find maximum of three number in C without using conditional statement and ternary operator
In the ANSI C standard 3.3.13:
Unlike the bitwise binary & operator, the && operator guarantees
left-to-right evaluation; there is a sequence point after the
evaluation of the first operand. If the first operand compares equal
to 0, the second operand is not evaluated.
There is an equivalent statement in the C++ standard
&& (and ||) establish sequence points. So the expression on the left-hand side will get evaluated before the right-hand side. Also, yes, if the left-hand side is false/true (for &&/||), the right-hand side is not evaluated.
What does the standard say about evaluating && expressions - does it guarantee that evaluation of parameters will stop at the first false?
Also, is the order of evaluation guaranteed to be the order of appearence in the clause?
5.14/1. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.
This only works for the standard && operator, user defined overloads of operator && don't have this guarantee, they behave like regular function call semantics.
Might the optimizer change something like:
if ( p->doSomethingReallyExpensive() && x == 3 )
to a form where it evaluates x==3 first?
An optimizer may decide to evaluate x == 3 first since it is an expression with no side-effects associated if x is not modified by p->doSomethingReallyExpensive(), or even evaluate it after p->doSomethingReallyExpensive() already returned false. However, the visible behavior is guaranteed to be the previously specified: Left to right evaluation and short-circuit. That means that while x == 3 may be evaluated first and return false the implementation still has to evaluate p->doSomethingReallyExpensive().
I recently came across something that I thought I understood right off the bat, but thinking more on it I would like understanding on why it works the way it does.
Consider the code below. The (x-- == 9) is clearly getting evaluated, while the (y++ == 11) is not. My first thought was that logical && kicks in, sees that the expression has already become false, and kicks out before evaluating the second part of the expression.
The more I think about it, the more I don't understand why this behaves as it does. As I understand it, logical operators fall below increment operations in the order of precedence. Shouldn't (y++ == 11) be evaluated, even though the overall expression has already become false?
In other words, shouldn't the order of operations dictate that (y++ == 11) be evaluated before the if statement realizes the expression as a whole will be false?
#include <iostream>
using namespace std;
int main( int argc, char** argv )
{
int x = 10;
int y = 10;
if( (x-- == 9) && (y++ == 11) )
{
cout << "I better not get here!" << endl;
}
cout << "Final X: " << x << endl;
cout << "Final Y: " << y << endl;
return 0;
}
Output:
Final X: 9
Final Y: 10
logical operators fall below increment operations in the order of
precedence.
Order of precedence is not order of execution. They're completely different concepts. Order of precedence only affects order of execution to the extent that operands are evaluated before their operator, and order of precedence helps tell you what the operands are of each operator.
Short-circuiting operators are a partial exception even to the rule that operands are evaluated before the operator, since they evaluate the LHS, then the operator has its say whether or not to evaluate the RHS, maybe the RHS is evaluated, then the result of the operator is computed.
Do not think of higher-precedence operations "executing first". Think of them "binding tighter". ++ has higher precedence than &&, and in the expression x ++ && y ++, operator precedence means that the ++ "binds more tightly" to y than && does, and so the expression overall is equivalent to (x++) && (y++), not (x++ && y) ++.
Shouldn't (y++ == 11) be evaluated, even though the overall expression has already become false?
No: the && and || operators short-circuit: they are evaluated left-to-right and as soon as the result of the expression is known, evaluation stops (that is, as soon as the expression is known to be false in the case of a series of &&, or true in the case of a series of ||)(*).
There is no sense in doing extra work that doesn't need to be done. This short-circuiting behavior is also quite useful and enables the writing of terser code. For example, given a pointer to a struct-type object, you can test whether the pointer is null and then dereference the pointer in a subsequent subexpression, for example: if (p && p->is_set) { /* ... */ }.
(*) Note that in C++, you can overload both the && and the || for class-type operands and if you do, they lose their short-circuiting property (it is generally inadvisable to overload && and || for this reason).
Precedence and associativity do not specify the order in which the operations are actually performed. They specify how operations are grouped: that is, in the following expression:
x && y++
...the lower precedence of && says that it is grouped as if it was:
x && (y++)
rather than as
(x && y)++
In your expression, the relative precedence of && and ++ do not matter, because you have separated those operators with parentheses anyway.
Grouping (and therefore precedence and associativity) specify what value each operator is operating on; but it specifies nothing about when it does so.
For most operators, the order in which the operations are performed is unspecified - however, in the case of && it is specified to evaluate the left hand operand first, then only evaluate the right hand operand if the result of the left hand operand was non-zero.
No. Order of precedence simply decides whether you get this:
A && B
(with A being x-- == 9 and B being y++ == 11) or
A == B == C
(with A being x--, B being 9 && y++, and C being 11).
Obviously, we're dealing with the first case. Short circuiting fully applies; if A is true, then B is not evaluated.
The conditional operators evaluate left-to-right and stop as soon as the result is known (an AND with a falsity or an OR with a true value).
C standard does not dictate any particular order of expression evaluation in if. So behavior will be compiler specific and using this style of coding not portable. You face that problem because incrementing/decrementing of value is post operation, but standard says as post operation of expression where variable is used. So if a compiler considers that your expression is just single variable usage as x or y, then you will see one result. If a compiler thinks that expression is entire if expression evaluation, then you will see other result. I hope it helps.