1

Possible Duplicate:
Could anyone explain these undefined behaviors (i = i++ + ++i , i = i++, etc…)

I am having this confusion in the Operator preference of && ++ and *****

int i=-1,k=-1,y,n;
y=++i*++i;
n=++k&&++k;
printf("%d %d %d %d",i,y,k,n);

output gcc : 1 1 0 0

here, for the case of y initially i is incremented once i.e i=0 and again i is incremented i.e i=1 now i*i is done i.e 25 since ++ has higher precedence than *

in second case k doesnot increment to 1 even though ++ has higher preference than && .Can anyone please explain this phenomenon??

Community
  • 1
  • 1
Debarshi Dutta
  • 442
  • 1
  • 5
  • 16
  • Why the downvotes? It's an interesting question. – D.C. Nov 15 '12 at 06:55
  • 1
    @darren it has been asked many times so it shows no research effort (see my comment about the possible duplicate). –  Nov 15 '12 at 06:56
  • Your experience with this code should lead you to resolve never to write code like this. – David Heffernan Nov 15 '12 at 06:59
  • It was from one of those technical questions based on C,not practical code. – Debarshi Dutta Nov 15 '12 at 07:01
  • 'initially i is incremented once i.e i=0 and again i is incremented i.e i=1 now i*i is done ' -- this assertion as to the order of things is not mandated by the language specification. Most compilers will actually generate code that does `++i` ... `++i` ... `y =i*i` – Jim Balter Nov 15 '12 at 07:06
  • 1
    @H2CO3: Actually I think this is a distinct question. The question here isn't really related to trying to modify the same variable; but operator precedence. Of course, the actual cause of the failure ends up being the sequence point problems; but just because two questions have the same answer doesn't mean that they are the same questions. – Billy ONeal Nov 15 '12 at 07:07
  • @Jim: Erm, on what are you basing "most compilers"? – Billy ONeal Nov 15 '12 at 07:07
  • @BillyONeal The question here is "I have this code, why this doesn't work as (expected) I would expect?" - this is exactly the same that is asked in the original question of which this is a duplicate. –  Nov 15 '12 at 07:08
  • @H2CO3: Hmm.. I suppose that's fair. – Billy ONeal Nov 15 '12 at 07:09
  • @BillyONeal 35 years of coding in C, including working on C compilers. If you have even a basic understanding of compiler technology, you will recognize that the naive implementation evaluates both subexpressions before evaluating the operator. Optimization can change that order, but only if it has a reason to. Some optimizers might produce `i += 2` ... `y =i*i`, still with a result of 1. – Jim Balter Nov 15 '12 at 07:23
  • @Jim: 35 years of coding in C is not a source here, considering your comment is about the generated code; not the parse tree. As for "compiler technology"; most of that is about parsing and semantic analysis, not code generation order. The parse tree, however, is unambiguous here; it is the code generation order that matters. – Billy ONeal Nov 15 '12 at 07:44
  • To clearify, what Jim is speaking of is the unspecified behavior "order of evalutation of subexpressions", which is another matter not necessarily related to the undefined behavior "modifying a variable twice between sequence points". As for a source for the former, see C11 6.5/3. – Lundin Nov 15 '12 at 07:48
  • @BillyONeal '35 years of coding in C is not a source here' -- whatever you say. I'm sure that there's no relation between the parse tree and the *order of evaluation*, which is, as you claim, a matter of parsing and not code generation. And that it's just a coincidence that the result is that y has the value of 1. – Jim Balter Nov 15 '12 at 07:49
  • @Lundin 'what Jim is speaking of' -- what I'm speaking of is the actual behavior of compilers, not the constraints that the standard doesn't put on them ... except for my initial statement that the OP's assertion about order is not to be found anywhere in the language standard. – Jim Balter Nov 15 '12 at 07:53
  • @JimBalter But the standard explicitly specifies the order to be unspecified :) It says that a complier may evaluate the left subexpression (of the "tree") first, or the right one, and it doesn't need to follow this order consistently, but can pick whatever is more suitable to optimize the code. And it doesn't need to document how it is done. – Lundin Nov 15 '12 at 07:57
  • @Lundin Yes, so? That the standard doesn't place some constraint on implementations has no bearing on what implementations actually do. In this case the left and right subexpressions are identical so the order that they are evaluated is irrelevant ... but most compilers will generate code to evaluate them before generating code to do the multiply, regardless of the fact that the standard doesn't say that they can't detonate the world's nukes instead. – Jim Balter Nov 15 '12 at 07:59
  • P.S. 'As for "compiler technology"; most of that is about parsing and semantic analysis, not code generation order.' -- This is an amusing statement. Regardless of what people may learn in freshman CompSci courses when writing toy parsers for interpreters, most compiler technology -- all the hard stuff -- is about code generation. – Jim Balter Nov 15 '12 at 08:09
  • Finally, a possible order of evaluation that gives a different result is `r1 = i + 1; y = r1*r1; i=r1; i=r1` – Jim Balter Nov 15 '12 at 08:16

7 Answers7

7

y = ++i * ++i; and similar clauses invoke undefined behavior, since they're modifying the variable more than once between two sequence points. You can expect anything to happen, literally.

n = ++k && ++k; is not actually UB, since the && operator is a logical AND operator and it is evaluated using short-circuiting - so there is a sequence point in that expression, but the other one is still wrong.

  • even if its an undefined behaviour ... how can the compiler give the same output everytime ?? – Debarshi Dutta Nov 15 '12 at 06:57
  • @DebarshiDutta Because the compiler is deterministic, perhaps? –  Nov 15 '12 at 06:58
  • Undefined here means undefined by the langugage specification. – David Heffernan Nov 15 '12 at 06:58
  • @DebarshiDutta not everytime. The compiled program is still assumed to be deterministic (unless it's JITted), but different compiled programs may do different things for the same code. – John Dvorak Nov 15 '12 at 06:58
  • kk actually was a bit confused about this thing.. thanks :) – Debarshi Dutta Nov 15 '12 at 06:59
  • 'You can expect anything to happen, literally.' -- Not rationally, and that's not what the standard says, despite the mythology around here. All the standard says is that **it** does not place any constraints on conforming implementations ... regardless of what the implementation does, it still conforms. But that doesn't mean that there aren't reasonable and rational expectations of what implementations will or won't do (for instance, they won't wipe your disk drive, even though they would be standard-conforming if they did). – Jim Balter Nov 15 '12 at 07:10
  • @JimBalter "they won't wipe your disk drive, even though they would be standard-conforming if they did" - that's why you should never assume anything. The Standard imposes **no** requirements, so **A. N. Y. T. H. I. N. G.** can happen. The Standard doesn't have the "rational" or "human expectations" terminology when talking about UB. –  Nov 15 '12 at 07:12
  • ' how can the compiler give the same output everytime ?? ' -- Why wouldn't it? Generate the assembly code and you will see that your compiler generates code to increment `i`, then increment it again, then square and store it in `y`. Other compilers might do something quite different, so the standard doesn't define the behavior. – Jim Balter Nov 15 '12 at 07:14
  • Sorry, but I was on the standards committee and I know full well what it says and what it means, and I correctly stated it above. The Rationale published along with the original the standard also refers to "quality of implementation", and one that capriciously wipes your disk drive just because the standard doesn't say it can't is very low quality. The reality is that there are NO conforming implementations because ALL implementations have bugs, but that doesn't mean you should never assume anything. Rationality is a matter of LOGIC that resides outside the standard. – Jim Balter Nov 15 '12 at 07:18
  • "one that capriciously wipes your disk drive just because the standard doesn't say it can't is very low quality" - we agree on that, although you still used wrong terminology. "The reality is that there are NO conforming implementations because ALL implementations have bugs, but that doesn't mean you should never assume anything" - yes, that is right, but that's in real life and not when answering a language-lawyer question. –  Nov 15 '12 at 07:24
  • We're talking about *rational expectations* -- that refers to real life, and real life only. As far as language lawyering, there is "undefined behavior" and that's it. Going beyond that with this nonsense about "anything can happen" is ... nonsense; it has nothing to do with the language standard, which makes no such assertion. The end. Sometimes I wonder whether the people who make these claims actually get paid to program or if they just work in universities. Paid, professional programmers go beyond language lawyering to *what programs* (including compilers) *actually do*. – Jim Balter Nov 15 '12 at 07:31
4

They are different here.

First, ++i*++i is undefined behaviour. There are two side effects and without a sequence point to split them.

Second, ++k&&++k is short circuit. Since ++k is 0. The expression should be 0. There is no need to evaluate the second ++k. It is important that && is a sequence point.

halfelf
  • 9,737
  • 13
  • 54
  • 63
3

The precedence of operators does not dictate the order in which those operators are evaluated. The order of evaluation is unspecified.

The precedence rules merely state that y=++i*++i; is interpreted as y=(++i)*(++i); and n=++k&&++k; as n=(++k)&&(++k);, but otherwise the compiler is allowed to actually execute them in any order.

There are a few exceptions to this rule; for instance, the standard mandates that the logical and operator (&&) short circuit; so the left side of such an expression must be evaluated before the right side.

For instance, consider the expression a = b+++c. Precedence rules mandate that this expression be interpreted as a = (b++) + c; but the compiler is freely allowed to execute this in any of the following orders:

  • add one to b and store the old value in a temporary, then add c and the temporary together, then store the result in a
  • add the values of b and c together, then store the result in a, then increment b
  • add the values of b and c together and store them in a temporary, then increment the value of b, then assign the temporary to a
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
2

This is undefined behaviour.Changing the value of a variable twice between two sequence points.

y=++i*++i; here you are modifying i twice between two sequence points.

For sequence points read this link

n=++k&&++k; but this is well defined behaviour because && is a sequence point. Here you are getting n == 0 because of short circuit of logical &&

Community
  • 1
  • 1
Omkant
  • 9,018
  • 8
  • 39
  • 59
  • Changing the value of a variable twice itself is not UB. Doing so between two segquence points is. –  Nov 15 '12 at 06:55
2

Precedence is irrelevant here.

In the first case: ++i * ++i, you have undefined behavior, because you're modifying i twice without an intervening sequence point.

In the second case, you have an &&, which defines a sequence point, so you have defined behavior. Specifically, with &&, the left operand is evaluated, then if and only if that yields a non-zero value, the right operand is evaluated. The result is the logical and of the two results. In your case, k starts out as -1, so when the ++k on the left is evaluated, the result is 0. Since it's 0, the right operand isn't evaluated at all.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

From my understanding of that what will happen is this:

(++k)    && (++k)    -->  ans1 && ans2
-1 -> 0      0 -> 1        0   &&  1   = 0
                              ^ The rest will not be calculated. 

So the ++k is evaluated first but still leads to a separate answer to the first ++k (because left to right order on same operators)

Therefore the answer will be 0.

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
0
y=++i*++i;

It is Undefined Behaviour (Language standard doesn't guarantee the output of variable will be same every time).

n=++k&&++k;

Since && is short-circuit operator and it won't operate 2nd operand if result can be determined by 1st operand itself. Since 1st operand becomes 0 result is false i.e. 0

Omkant
  • 9,018
  • 8
  • 39
  • 59
Azodious
  • 13,752
  • 1
  • 36
  • 71