This question already has answers here:
Why are these constructs using pre and post-increment undefined behavior?
(14 answers)
Closed 6 years ago.
Here is a question I can not explain clearly.
Which option is wrong and why?
(A) a += (a++);
(B) a += (++a);
(C) (a++) += a;
(D) (++a) += (a++);
What is the difference between A and B?
My understand:
A is a UB but B is OK because the side effect of ++a will be completed before the assignment. Is that right?
Update: What is the difference between ++a and a++ within the sequence point? The side effect of pre-increment(decrement) may be completed any time before the next seq-point just like post-increment(decrement)?
Which option is wrong and why?
All of them are wrong, and that's because the first two invoke undefined behavior, and the last two don't compile. (And if they did, they would invoke UB as well.)
The first and second is UB because C does not define what is supposed to be evaluated first.
The third and fourth do not compile - the reason: lvalue required as left operand of assignment
Let me be more specific on the first two:
a += (a++); - equal to a = a + (a++);
if the a++ is evaluated first then it's a = a + 1 + a;
if the a++ is evaluated at the end then it's a = a + a;
but C does not define what should happen first, so it depends on the implementation, and it's UB.
The same goes for the second.
Related
I know that:
int b = 1, c = 2, d = 3, e = 4;
printf("%d %d %d", ++b, b, b++);
results in undefined behavior. Since
Modifying any object more than once between two sequence points is UB.
Undefined behavior and sequence points
But I don't know if:
int b = 1, c = 2, d = 3, e = 4;
printf("%d", b++ + ++c - --d - e--);
is also UB?
What I think is that increment/decrement operators will evalute first because of the precedence, between them right to left since the associativity . Then arithmetic operators will be evaluated left to right.
Which will just be
(b) + (c + 1) - (d - 1) - (e)
that is, 1 + (2 + 1) - (3 - 1) - (4)
= (2 - 4)
= -2
Is it right?
But I don't know if: ... is also UB?
It is not, but your reasoning about why is fuzzy.
What I think is that increment/decrement operators will evaluate first because of the precedence, between them right to left since the associativity . Then arithmetic operators will be evaluated left to right.
Precedence determines how the result is calculated. It doesn't say anything about the ordering of the side-effects.
There is no equivalent of precedence telling you when the side effects (the stored value of b has been incremented, the stored value of e has been decremented) are observable during the statement. All you know is that the variables have taken their new values before the next statement (ie, by the ;).
So, the reason this is well-defined is that it does not depend on those side-effects.
I deliberately hand-waved the language to avoid getting bogged down, but I should probably clarify:
"during the statement" really means "before the next sequence point"
"before the next statement (... ;)" really means "at the next sequence point"
See Order of evaluation:
There is a sequence point after the evaluation of all function arguments and of the function designator, and before the actual function call.
So really the side-effects are committed before the call to printf, so earlier than the ; at the end of the statement.
There is a gigantic difference between the expressions
b++ + ++c - --d - e--
(which is fine), and
x++ + ++x - --x - x--
(which is rampantly undefined).
It's not using ++ or -- that makes an expression undefined. It's not even using ++ or -- twice in the same expression. No, the problem is when you use ++ or -- to modify a variable inside an expression, and you also try to use the value of that same variable elsewhere in the same expression, and without an intervening sequence point.
Consider the simpler expression
++z + z;
Now, obviously the subexpression ++z will increment z. So the question is, does the + z part use the old or the new value of z? And the answer is that there is no answer, which is why this expression is undefined.
Remember, expressions like ++z do not just mean, "take z's value and add 1". They mean, "take z's value and add 1, and store the result back into z". These expressions have side effects. And the side effects are at the root of the undefinedness issue.
This question already has answers here:
Why are these constructs using pre and post-increment undefined behavior?
(14 answers)
Closed 1 year ago.
Why i can't do the following in Objective-C?
a = (a < 10) ? (a++) : a;
or
a = (a++)
The ++ in a++ is a post-increment. In short, a = a++ does the following:
int tmp = a; // get the value of a
a = a + 1 //post-increment a
a = tmp // do the assignment
As noted by others, in C this is actually undefined behavior and different compilers can order the operations differently.
Try to avoid using ++ and -- operators when you can. The operators introduce side effects which are hard to read. Why not simply write:
if (a < 10) {
a += 1;
}
To extend Sulthan's answer, there are several problem with your expressions, at least the simple assignment (case 2).
A. There is no sense in doing so. Even a++ has a value (is a non-void expression) that can be assigned, it automatically assigns the result to a itself. So the best you can expect is equivalent to
a++;
The assignment cannot improve the assignment at all. But this is not the error message.
B. Sulthans replacement of the statement is a better case. It is even worse: The ++ operator has the value of a (at the beginning of the expression) and the effect to increment a at some point in future: The increment can be delayed up to the next sequence point.
The side effect of updating the stored value of the operand shall occur between the previous and the next sequence point.
(ISO/IEC 9899:TC3, 6.5.2.4, 2)
But the assignment operator = is not a sequence point (Annex C).
The following are the sequence points described in 5.1.2.3:
[Neither assignment operator nor ) is included in the list]
Therefore the expression can be replaced with what Sulthan said:
int tmp = a; // get the value of a
a = a + 1 //post-increment a
a = tmp // do the assignment
with the result, that a still contains the old value.
Or the expression can be replaced with this code …:
int tmp = a; // get the value of a
a = tmp // assignemnt
a = a + 1 // increment
… with a different result (a is incremented). This is what the error message says: There is no defined sequence (order the operations has to be applied.)
You can insert a sequence point using the comma operator , (what is the primary use of it), …
a++, a=a; // first increment, then assign
… but this shows the whole leak of meaning of what you want to do.
It is the same with your first example. Though ? is a sequence point …:
The following are the sequence points described in 5.1.2.3:
… The end of the first operand of the following operators: […] conditional ? (6.5.15);[…].
… both the increment (a++) and the assignment (a=) are after the ? operator is evaluated and therefore unsequenced ("in random order") again.
To make the comment "Be careful" more concrete: Don't use an incremented object in an expression twice. (Unless there is a clear sequence point).
int a = 1;
… = a++ * a;
… evaluates to what? 2? 1? Undefined, because the increment can take place after reading a "the second time".
BTW: The Q is not related to Objective-C, but to pure C. There is no Objective-C influence to C in that point. I changed the tagging.
This question already has answers here:
Why are these constructs using pre and post-increment undefined behavior?
(14 answers)
Closed 6 years ago.
testing some code when studying C language,
#include <stdio.h>
#include <math.h>
#define hypotenusa(x, y) sqrt((x) * (x) + (y) * (y))
int main(void) {
int a, x;
x = 2;
a = hypotenusa(++x, ++x);
printf("%d\n", a);
}
And I am getting the answer
6 in one program(dosbox gcc compiler)
7 in codelight gcc compiler and
8 on codeChef online compiler
can anyone explain this behaviour?
my logic says it should be 6 (sqrt(42)) but...
It's undefined behaviour.
After the macro replacement
a = hypotenusa(++x, ++x);
becomes:
a = sqrt((++x) * (++x) + (++x) * (++x));
As you can see x is modified multiple times without any intervening sequence point(s). See What Every C Programmer Should Know About Undefined Behavior.
hypotenusa(++x, ++x) is undefined behavior.
It is up to the compiler which of the parameters gets incremented (and pushed) first - after the macro expansion, there are a total of four instances, and the sequence is not defined.
You should never increment the same variable multiple times in the same statement, to avoid this kind of issues. Using a variable twice in a macro can hide this error and make it really nasty.
The behavior of your macro is undefined, meaning any result is possible.
Chapter and verse
6.5 Expressions
...
2 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. If there are multiple allowable orderings of the
subexpressions
of an expression, the behavior is undefined if such an unsequenced side
effect occurs in any
of the orderings.84)
84) This paragraph renders undefined statement expressions such as i = ++i + 1;
a[i++] = i;
while allowing i = i + 1;
a[i] = i;
Basically, if you modify the value of an object more than once in an expression, or both modify an object and use its value in a computation in an expression, the result will not be well-defined unless there is a sequence point in between those operations. With a few exceptions, C does not force left-to-right evaluation of expressions or function parameter evaluations, nor does it mandate that the side effect of ++ or -- be applied immediately after evaluation. Thus, the result of an expression like x++ * x++ will vary from platform to platform, program to program, or even potentially from run to run (although I've never seen that in practice).
For example, given the expression y = x++ * x++, the following evaluation sequence is possible:
t0 <- x // right hand x++, t0 == 2
t1 <- x // left hand x++, t1 == 2
y <- t0 * t1 // y = 2 * 2 == 4
x <- x + 1 // x == 3
x <- x + 1 // x == 4
which doesn't give you the result you expect if you assume left-to-right evaluation.
This question already has answers here:
Why doesn't a+++++b work?
(9 answers)
Closed 8 years ago.
The following code prints 7. It increments value of a, but b remains unaffected. Post increment operator is not supposed to increment first and then use the value which it has done in this case. It should be the other way around. Why this change in behaviour? Also, and perhaps more importantly, why is b not pre-incremented?
int main() {
int a=5, b=2;
printf("%d", a+++b);
}
You have a well defined behavior. Where
a++ + b = 5 +2 = 7
If you have a pre-incrementor operator like
printf("%d\n",++a+b);
Then the output would be 8
The ++ operator has higher precendence than the + unary plus operator so the evaluation from left to right of
a+++b will happen
a++ + b which will fetch you 7
Post-incrementation is done by taking the value of your variable first which in this case is 5 so
a= 5 and b=2
In your code a is added to b and then it is incremented
This question already has answers here:
Why are these constructs using pre and post-increment undefined behavior?
(14 answers)
Closed 3 years ago.
I am new to C, i have an Increment operator program in C
#include<stdio.h>
main(){
int a, b;
a = 2;
b = a + ++a + ++a;
printf("%d", b);
getchar();
}
The output is 10, can someone explain me how the output will be 10 .
a + ++a + ++a;
Behaviour for this is undefined.
The compiler might generated code that evaluated this as 2 + 4 + 4 or 3 + 3 + 4, but any combination/ordering of incrementing and accessing is a "valid" result.
This is undefined, the ++i can happen in any order.
Function call arguments are also ambigiously evaluated, e.g. foo(++i,++i).
Not all operator chains are undefined, a||b||c is guaranteed to be left-to-right, for example.
The guarantees are made in places known as sequence points although this terminology is being deprecated and clarified in C++0x.
What's odd in your example is that neigher 2+3+4 nor 4+4+3 happened, so the compiler evaluated the left side first in one step and the right side first in the other. This was probably an optimisation to flatten the depencency graph.