A tricky C problem

Started by
19 comments, last by greenhybrid 16 years, 10 months ago
The question is : int a=10; a+=a-=a*=a; Put these statements into a test driver and execute that program I find that the result is 0.How can it be?Can you please tell me what happens?I evaluate the result to be 100,since +=,-=and*= have the same priority.Thank you.
Advertisement
The result is undefined - you can't do more than one assignment per sequence point. In this case it's probably doing

a+=a (a=20)
a-=a (a=0)
a*=a (a=0)

but you can't rely on this - the compiler could legitimately do whatever it wanted
Sequence point in Wikipedia
Quote:Original post by cshowe
The result is undefined - you can't do more than one assignment per sequence point.


No, you forgot to do your homework, multiple assignments are very well possible; operator precedence, esp. the assignment operators are to be evaluated "from-right-to left" (contrary to the 'normal' left-to-right order).

Next time please prefix your post with "I think" or "In my humble opinion" rather than saying "It is".

 a=10; a+=a-=a*=a;


is the same as:

 a+=(a-=(a*=a));


let's partition that:

a=10;a*=a; //> a is now 100a-=a; //> == "100-100", so after that op a is 0a+=a; //> == "0+0", easy, not?



Hope that helps you, baixiangzhlt :)


edit: btw, what I find comfortable from time to time is a construct like this:

int res;if( 0 != (res=foo()) ){ ...}else{  return res;}//> or evenif( res=bar() ){ ...}else{  return res;}//> this one is very useful:x[0] = x[1] = x[2] = 0; //> saves a lot of lines and helps the compiler for SIMD
Quote:Original post by greenhybrid
Quote:Original post by cshowe
The result is undefined - you can't do more than one assignment per sequence point.


No, you forgot to do your homework, multiple assignments are very well possible;


You are probably right, but for the wrong reasons.

Multiple assignments (or, more generally, modifications) per sequence point are generally evaluated in an unspecified order which is independent of operator precedence. Consider:

int a = 1;return (a += 2) + (a *= 2);


The order of precedence is fully specified through means of parentheses, yet we do not know whether this code returns 6 or 9.

Chained assignments (as opposed to multiple assignemnts), do not have this problem. I do not know for chained modifications. The standard probably specifies how the += operators act, but I don't have it with me right now. The risk is having:
a = 1;a += (a *= 2);// Equivalent to:a = (a + (a = (a * 2)));


Evaluated as:
a = (1 + (a = (1*2)); // --> a = 3


EDIT: apparently, it's undefined even for chained modifications, if you modify the same variable twice.

[Edited by - ToohrVyk on May 25, 2007 7:15:23 AM]
Quote:Original post by baixiangzhlt
The question is :
int a=10;
a+=a-=a*=a;
Put these statements into a test driver and execute that program I find that the result is 0.How can it be?Can you please tell me what happens?I evaluate the result to be 100,since +=,-=and*= have the same priority.Thank you.


This one seems easy enough; the compiler looks at the code as sub sections.

a + (a - (a * a))
a - (a * a)
a * a;

so looking at this, a*a = 100 (there you are correct); however a (as it now exists) would be 100 and thus would be 100 - 100 = 0. Then you take the last portion of 0 + 0 = 0 where the actual result shows.

-Root
Quote:Original post by ToohrVyk
Quote:Original post by greenhybrid
Quote:Original post by cshowe
The result is undefined - you can't do more than one assignment per sequence point.


No, you forgot to do your homework, multiple assignments are very well possible;


...

Multiple assignments (or, more generally, modifications) per sequence point are generally evaluated in an unspecified order which is independent of operator precedence. Consider:

int a = 1;return (a += 2) + (a *= 2);


The order of precedence is fully specified through means of parentheses, yet we do not know whether this code returns 6 or 9.

...


The compiler is very well defined by the means of operator precedence, that is operator priority as well as whether the sub-statements are evaluated left-to-right or right-to-left.

Since add's go left-to-right, at first the left term (a+=2) is evaluated, and then the term on the right, (a*=2).

1) Let a=1
2) Evaluate left term, evaluate expression in brackets, have a = a+2 = 3 now
3) Evaluate right term, evaluate expression in brackets, with a=3, we have a = a*2 = 6 now


Quote:Original post by greenhybrid
The compiler is very well defined by the means of operator precedence, that is operator priority as well as whether the sub-statements are evaluated left-to-right or right-to-left.


Operator precedence (or associativity) has nothing to do with evaluation order—precedence only dictates how the abstract syntax should be built, it does not impose any sense of priority or timing over the various branches of that syntax tree at evaluation time.

In particular, in an expression such as a + b, a may be evaluated before or after b depending on compile-time and even runtime considerations—the standard is pretty clear on allowing compiler writers this freedom. See this FAQ entry, for instance.

Quote:Since add's go left-to-right, at first the left term (a+=2) is evaluated, and then the term on the right, (a*=2).


I'd be curious to know where you got this information (the "add's go left-to-right" part) from in the first place. Are you sure you're not confusing this with associativity? Because adds are, indeed, left-to-right associative, but associativity has no bearing here (it only plays a role when you're adding more than three subexpressions together).
If you take out the a-= portion of the equation, thus leaving you with

a+=a*=a;

You'll notice that the answer becomes 200. Once again proving that a+= is probably done first however the compiler must find out what a*=a is before it can add it to itself.
Quote:If you take out the a-= portion of the equation, thus leaving you with

a+=a*=a;

You'll notice that the answer becomes 200. Once again proving that a+= is probably done first however the compiler must find out what a*=a is before it can add it to itself.


That doesn't "prove" anything, except perhaps the behaviour of one *particular* compiler version with a *particular* set of parameters.

The behaviour of compilers doesn't matter. What matters is what the *Standard* says.

It's very simple. *ANY* side effect in an expression ins *ONLY* guaranteed by the Standard to be completely executed at the next sequence point. The value of the expression (a+=4) *will* always be a+4, BUT the value of *a* is not necessarily updated before the next sequence point. Thus, writing to a and then reading its value without a delimiting sequence point (as happens in the OP's example), will trivially yield an implementation-defined value.

This topic is closed to new replies.

Advertisement