Click here to Skip to main content
15,114,596 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
int a = 3;
int b = 5;
int c = 40;
int d = c-- - b * a;
Console.WriteLine($"a={a}, b={b} c={c} d={d}");

an order of "c-- - b * a" expression handling expected like this :

39 15
int d = (c--)-(b*a); == 24? of cource not! d=25!

because in fact an execution order is following:

int d = c-(b*a); and c-- is postdecrement operation and lost somehow

so thiese expressons are equal

int d = c-(b*a) and
int d = (c--)-(b*a)

Could somebody explain how?

and moreover!

if you'll try to use QuickWatch on (c-- - b * a), it fulfilles the procedure in the background and changes the c so when you try to use it again, the result will be 24! because the c has being changed.
I believe it is not correct
because QuickWatch should execute the expression, but in the different place somewhere and shouldn't change the variable.

What I have tried:

simple arythmetic question C#.
Posted
Updated 3-Feb-20 4:10am
Comments
Richard Deeming 4-Apr-19 12:01pm
   
Any code which tries to be "clever" by mixing an increment/decrement operator into a larger set of operations in a single statement should be thrown out. There's absolutely no benefit to doing it; it just makes your code harder to read. Even developers with lots of experience of "clever" code will have to stop and think to work out what it's supposed to be doing.

They aren't the same. c-- is a postfix operator which changes c after it's been used.
You need to be very careful when using these, as the results you get may not be what you expected. See here: Why does x = ++x + x++ give me the wrong answer?[^]

Quote:
Thank you so much! I've read an article and found the "Precedence and Order of Evaluation" from MSDN.
For the moment the precedence of operators doesn't answer the question "why"
for example
int a = 5;
int b = 3;
int i = 10;
int x = (i++) + a * b;
logically (if we agree with precedence) i should be 11 (!) and x = 26, not 25, because i++ is placed in brackets.
but the ++ operator is still omit eventually, no matter on evaluation direction


No, you are wrong.
C#
int x = i++ + a * b;
Is using a post fix increment, which says, "use the current value of i and then add one to i"
If you write it long hand, you get this:
C#
int i2 = i;
i += 1;
x = i2 + a * b;
which is:
C#
25 == 10 + 3 * 5;
That's why mixing auto increment and decrement operators in the same expression is so fraught: there are no universal rules to say exactly when the increment should be done.
Think of this, assuming i is 10
C#
x = i++ + i;
What is the value of x?
20?
21?
22?

The "right" answer is 21, and i ends up as 11.
C#
int i2 = i;
i += 1;
x = i2 + i;
But ... it could easily be 20 if strictly evaluated from right to left:
C#
x = i + i;
i += 1;
   
v2
Comments
[no name] 2-Apr-19 4:18am
   
Hi!
put thiese two expressions in c# and execute.
The result will be equal
OriginalGriff 2-Apr-19 5:20am
   
The result in "d" will be the same, which is the whole point - but teh value in "c" will not!
A post decrement in the middle of an expression is perfectly legal - and even sometimes wanted. It has a "side effect" that c is decremented, but it's perfectly legal and will work.
There is no warning, because it does what it is expected and designed to do!
[no name] 2-Apr-19 5:30am
   
the question was not about the syntax or uderstanding of an operation execution.
I meant the following:
(look at Solution_2 log)

"look, you should agree, this is not normal situation
int d = c--; // the "c--" is in the end of expression. I agree when the expression is closed after this, the postdecrement operation works as expected and in this case "d==c"

int d = c-- - b * a; //but in this case "c--" is in the middle of an expression and apparently should be executed totally to return the value to the expression it participates

The question is in evidence of an execution
OriginalGriff 2-Apr-19 5:50am
   
No, you don't understand. It's a perfectly normal situation.

Pre- and post-fix operators can be used on any lvalue at any point in an expression, they aren't fixed to the "end" of an expression (particularly since many compilers evaluate expressions right to left not left to right, when the order isn't important)

Doesn't matter if you don't "think they should" - they can, just like any other unary operator. Or are you suggesting that "a * -b * c" shouldn't be allowed as well? :laugh:

And the real problem is that it is a "side effect" operator, so you shouldn't write complicated expressions using them. But there is nothing stopping you doing that (other than the ultimate results are not defined in many languages and may change without notice).
[no name] 2-Apr-19 7:12am
   
Oh! And That Is quite interesting:
"particularly since many compilers evaluate expressions right to left not left to right, when the order isn't important"
I consider this answer "why" for me!
I did'n know that.

Thank you!
OriginalGriff 2-Apr-19 7:23am
   
You're welcome - it's explained in the link I gave you. Search for "Precedence and Order of Evaluation" and you'll see it.
[no name] 2-Apr-19 8:52am
   
Thank you so much! I've read an article and found the "Precedence and Order of Evaluation" from MSDN.
For the moment the precedence of operators doesn't answer the question "why"
for example
int a = 5;
int b = 3;
int i = 10;
int x = (i++) + a * b;
logically (if we agree with precedence) i should be 11 (!) and x = 26, not 25, because i++ is placed in brackets.
but the ++ operator is still omit eventually (not omit of cource but executed after the expression), no matter on evaluation direction
OriginalGriff 2-Apr-19 9:18am
   
Answer updated.
[no name] 2-Apr-19 9:40am
   
>>No, you are wrong

Oh, really?!

int x = i++ + a * b;

Haven't you forgot the brackets?
Shouldn't it be like that:

int x=(i++) + a * b;

in my beliefe in this case it would be:

i += 1;
x = i + a * b;

because the brackets are in main precedence... ?

May be (and I'm sure) you're right because I can see that in C# compiler x==25!
But why?!
OriginalGriff 2-Apr-19 9:57am
   
The brackets don't matter - they only affect operator precedence, not evaluation order.
Remember: "Only the sequential-evaluation (,), logical-AND (&&), logical-OR (||), conditional-expression (? :), and function-call operators constitute sequence points and therefore guarantee a particular order of evaluation for their operands."
They act as a sequence point only within the brackets.
Think about it: 2 + 3 is the same as 3 + 2, yes? That's fundamental to math (and called Associativity you will remember from childhood).
So when you write x = (y) + (z) the compiler is at liberty to evaluate (y) or (z) first because it doesn't matter to the result of the addition as long as y and z are both fully evaluated before the addition. Same goes for multiplication!
If you write x = (y + z) + a then you are saying "add y and z, then add a, but the compiler can ignore the brackets because they don't change anything.
If you write x = func(y + z) + a then the function brackets act as a sequence point and tell the compiler that y, z, and y + z must all be fully evaluated before you call func, but even that has no problem evaluating "a" first (just because it feels like it) because it has "no effect on the result"

Only a very few operators establish an evaluation sequence: && and || because the specification lays down the law: "the left hand side of &&, ||, and ? must be evaluated before the right hand side"
That law does not exist for +, -, * , or /

Do not confuse precedence with evaluation order, they are not the same thing (or even related!)
[no name] 2-Apr-19 10:03am
   
Yup!
I'm trying to explore the ILDasm and I can see the compiler totally ignores the brackets - the listings are completely the same with brackets or without

"If you write x = func(y + z) + a then the function brackets act as a sequence point and tell the compiler that y, z, and y + z must all be fully evaluated before you call func, but even that has no problem evaluating "a" first (just because it feels like it) because it has "no effect on the result"

but if it so, the compiler should fully(!) evaluate the func(I++) first and than the rest of expression.
But obviously it doesn't
OriginalGriff 2-Apr-19 10:25am
   
Why? It has no effect on the result.
If funcA(...) + funcB(...) returns 123, what does it matter that funcA returns 100 and funcB returns 23? The addition operator doesn't care because addition is associative, and it can be written funcB(...) + funcA(...) and give the same result.

And so it's up to the compiler to do exactly what is convenient for it - which may not be what you think. Worse, the optimization phase of release compilation can *change that order* because it can "see" common expressions that it will only work out once, or reorder the whole expression in order to keep everything in a limited set of machine registers. You have no control over that, and that's why if you want to do something that relies on evaluation order you have to break it into separate lines to force the compiler to consider sequence points. And that's why you have to be very careful with "side effect" operations (particularly in property getters) or things can get very confusing, very quickly.

You are still confusing precedence with order, and they still aren't the same thing!
[no name] 2-Apr-19 10:53am
   
>>You are still confusing precedence with order, and they still aren't the same thing!
I doubt...
I believe we are talking about the different things.

I++ - is the bunch of procedures (placing in stack, incrementing, adding and etc.) right?
"function brackets act as a sequence point and tell the compiler that y, z, and y + z must all be fully evaluated before you call func"

That mean, all the procedures of this bunch have to be evaluated and the stack should be restored.
So the ++ - is not an ephemeral thing, is the procedure which has to return the result (in this case)

int i = 10;
int x = (i++) +1;

as you can see there are no any other "i" out of the brackets.
what order or precedence we discuss here?
OriginalGriff 2-Apr-19 11:01am
   
No, it's a unary operator that has a side effect.
you can think of pre- and post-fix increment as functions:

public static int PlusPlusPrefix(ref int x)
{
x += 1;
return x;
}
public static int PlusPlusSuffix(ref int x)
{
int y = x;
x += 1;
return y;
}

And you might see what they do. (But ... don't rely on it, the compiler does not have to treat it like that - and not all compilers do!)
[no name] 2-Apr-19 11:13am
   
>>No, it's a unary operator that has a side effect.
from the ILDasm perspective it's not.
So how does it come an increment result is placed in the different stack place?
... hmm..
I should explore the ILDasm of ++I...
As you are stating yourself, you are using a postdecrement - so the value of c will change after it is used in your expression.

So
d=(c--)-(b*a);
is identical to:
d=c-b*a; c=c-1;
Just like
d=(--c)-(b*a);
is identical to
c=c-1;d=c-b*a;
It only gets really fun when you have multiple of them operating on one variable in one statement. Fun to play with, but never used in production code so personally I just forgot how that works.
   
Comments
[no name] 2-Apr-19 4:15am
   
Hi!
An explanation is clear.
Asking my question, I meant the operation in this case is not obvious.
It should be the warning mechanism in c# to make is more obvious
lmoelleb 2-Apr-19 4:19am
   
The operation is doing exactly what this operation has been doing for many decades across multiple languages - so what exactly should the warning be? Something like "If you do not know how this operation works it might give a different result than you expect"... but that goes for everything. :)
[no name] 2-Apr-19 4:32am
   
look, you should agree, this is not normal situation
int d = c--; // the "c--" is in the end of expression. I agree when the expression is closed after this, the postdecrement operation works as expected.
int d = c-- - b * a; //but in this case "c--" is in the middle of an expression and apparently should be executed to return the value to the expression it participates

.But still.. You right "this operation has been doing for many decades across multiple languages"
lmoelleb 2-Apr-19 4:37am
   
Personally I prefer avoiding the style, but I would not go so far as saying it is not normal, nor that it should be avoided in all cases. Maybe you can find (or write) a code analyzer if you prefer avoiding it? That way you will get a warning.
[no name] 2-Apr-19 4:40am
   
See, "Personally I prefer avoiding the style" - that is what I meant.
lmoelleb 2-Apr-19 4:42am
   
I do not consider my personal preference to be the definition of "normal". :D
But as mentioned - if you really want a warning here, that would be something for a code analyzer, not the core compiler.
[no name] 2-Apr-19 4:44am
   
Ahh... let's consider this just like exchanging of thaughts
Quote:
I believe it is not correct
Your belief is wrong. See, for instance: Increment and decrement operators - Wikipedia[^].
   
Interesting. I've never tried a question like this.

This is only a shot in the dark, but do you think the program is reading
"(C--) - (a*b)"
as
"(C(-1)(-1)) - (a*b)"?

I know it's a stretch, but if there's two negatives right next to an int, perhaps they're both turned into (-1)? If that's true, then putting them together multiplies into a multiple of +1:

d = (c--) - (a*b)
d = (c(-1)(-1)) - (3*5)
d = (c(1)) - (15)
d = 40 - 15
d = 25
   
Comments
Richard Deeming 3-Feb-20 10:20am
   
That's not what's happening. Have a read of the previous solutions, which explain what the problem is.
Jay-Mickey 4-Feb-20 12:02pm
   
Ah, I see now.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900