Skip to main content
GameDev.net gamedev.net
🔒 Locked

Undefined behaviour

Started by TEUTON Apr 9, 2006 at 5:04 AM 17 replies 950+ views
Original Post
TEUTON
TEUTON
If this
a=i++;


is undefined. Why is this not undefined
i=i+1;


is not undefined I know the reason for first...it's relating to sequence point but don't understand about second.
Trap
Trap
What language? What's defined and whats undefined varies with languages.
TEUTON
TEUTON
C/C++
haegarr
haegarr
i = i + 1
You could image that a temporary variable is created and gets the value of i+1 assigned. Since the whole RHS is calculated at that moment, the value of the temp variable is assigned to the (LHS) i. So the usages of i on RHS and on LHS are clearly decoupled.

If using the post-increment operator on RHS, the value of i (I don't mean a temporary copy here) is changed besides the explicit assignment. So the effect depends on whether i is changed before or after the assignment takes places.
Trap
Trap
I still haven't bought a copy of the standard, so I can't tell you what the language standard says about both situations.

A vague hint:
The target of an assignement can be something that has to be computed and there is no ordering between the computation of the assignement-target and the computation of the value that should be assigned to the target.

In your first example there a 2 expressions that need to be evaluated: "a" and "i++". In your second example there ist only 1: "i+1"
TEUTON
TEUTON
Quote:
Original post by haegarri = i + 1
You could image that a temporary variable is created and gets the value of i+1 assigned. Since the whole RHS is calculated at that moment, the value of the temp variable is assigned to the (LHS) i.

So the usages of i on RHS and on LHS are clearly decoupled.


Doesn't that apply on first expression?
i++ being assigned to temporary and then to a.
Trap
Trap
It doesn't matter what you think it should be, only what's written in the standard is important.
haegarr
haegarr
Quote:
Original post by Trap
It doesn't matter what you think it should be, only what's written in the standard is important.

Of course, but also the operator precedence is part of the standard.


EDIT: Ups, now the other post is deleted accidentally. Hence once again:

My gcc means
a = i++
is equivalent to
a[i+1] = i
i = i+1

That is, as TEUTON has mentioned, like the temporary copy is assigned, but the increment operator is applied before the assignment (as the operator precedence tells).

Seems be a reasonable result. However, _if_ this behaviour is really undefined, than other compiler may show other results, of course.
Trap
Trap
Sure, operator precedence is part of the standard, but it doesn't help you in the first situation.

a=i++;

You have 2 parts that need to be evaluated: a and i++. There is no ordering defined between these two evaluations. That is why it is undefined.
haegarr
haegarr
Quote:
Original post by Trap
A vague hint:
The target of an assignement can be something that has to be computed and there is no ordering between the computation of the assignement-target and the computation of the value that should be assigned to the target.

Besides the operator precedence there is also an operator associativity. And that is defined to be R-L (i.e. right first, then left) for all assigning operators.

EDIT: As a consequence I don't see why
a = i++
should not be defined at all. It seems me that all parts in question are actually defined, or do I miss something?
Trap
Trap
Does associativity imply an ordering between evaluation of different parts of an expression?
joanusdmentia
joanusdmentia
Quote:
Original post by haegarr
Quote:
Original post by Trap
A vague hint:
The target of an assignement can be something that has to be computed and there is no ordering between the computation of the assignement-target and the computation of the value that should be assigned to the target.

Besides the operator precedence there is also an operator associativity. And that is defined to be R-L (i.e. right first, then left) for all assigning operators.

EDIT: As a consequence I don't see why
a = i++
should not be defined at all. It seems me that all parts in question are actually defined, or do I miss something?


Operator grouping is used to identify sub-expressions, it's clear to see that there are 2 sub-expressions here, a and i++. However operator grouping doesn't have anything to do with the order in which independant sub-expressions are evaluated, and to quote the standard....

Quote:
Except where noted, 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.


Thus, the result of expression a = i++; is undefined.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The nly verdict is vengeance; a vendett o
CTar
CTar
Quote:
Original post by haegarr
My gcc means
a = i++
is equivalent to
a[i+1] = i
i = i+1


This is what happens when you use something which is undefined. What is most likely happening here is that we have two expressions
a and i++
GCC starts evaluation the right hand side, so now old i = i+1, so the expressions look like this:
a[i+1] and i
Even though some beginner tutorials tell you, i++ is NOT equivilant to:
ii = i+1

In most cases the functionality is equal though, that is why they are set to be equal. A compiler is allowed to define i++ like this
(i=i+1)-1;
Which gives the same functionality. Also if I remember correctly the original code is not supposed to result in undefined behavior, the order of the evaluations is just undefined.

Quote:
C/C++

That is not a language, your language is either C or C++. In some cases there is a difference in the behavior promised by the C and C++ standard so you should say which one you use. In this case though I don't think the language matters.
TEUTON
TEUTON
Quote:
Original post by CTar
That is not a language, your language is either C or C++. In some cases there is a difference in the behavior promised by the C and C++ standard so you should say which one you use. In this case though I don't think the language matters.

I assumed it won't matter that is why I said C/C++.



MaulingMonkey
MaulingMonkey
Quote:
Original post by joanusdmentia
Quote:
Original post by haegarr
EDIT: As a consequence I don't see why
a = i++
should not be defined at all. It seems me that all parts in question are actually defined, or do I miss something?


Operator grouping is used to identify sub-expressions, it's clear to see that there are 2 sub-expressions here, a and i++. However operator grouping doesn't have anything to do with the order in which independant sub-expressions are evaluated


Indeed. To elaborate, let me use:

a = ++i = ++i;

To underscore the difference, I'm going to replace this with a string of functions:

int & foo() { return a; }
int & bar() { return ++i; }
int & baz() { return ++i; }
foo() = bar() = baz();

The associativity rules do is to have the final statement interpreted as such:
foo() = (bar() = baz());

This ensures that the result of baz() will be assigned to the result of bar(). What it does not ensure is which function will be called first. If the compiler wants to, it can call foo() first, store the reference as an un-named temporary value, and then proceed to evaluate baz(). On the other hand, it could choose to call baz() first, in which case the element of the array returned by foo() will be different!


TEUTON: The second one is somewhat hard to explain if it's not intuitively obvious, but I'll try!

Again, we can seperate things out into functions:

int & foo() { return i; }
int bar() { return i + 1; }
foo() = bar();

The key factor here is that calling either of our "subfunctions" does not change the result of the other, the order in which they are called does not matter. While the actual assignment does change the value of i, the assignment in this case cannot take place before both of it's operands have been evaluated.

This isn't the case in the foo() = bar() = baz() case - the problem lies in that:

1) foo() = ....
It matters wheither foo() or the rest is evaluated first (the result of foo depends on the order)

2) ... bar() = baz()
It matters wheither bar() or baz() is evaluated first (the result of both bar and baz on the order)


Tricker rules come into play with built in types such as int. You might expect this to print out 3:

int i = 1;cout << (<font color="red">i++</font> + <font color="blue">i++</font>) << endl;


The argument goes that, while i++ could "return" in either 1 or 2, depending on wheither or not it or i++ was evaluated first, that i++ would be guaranteed to be the other.

However, the standard writers were far more masochistic than that. When it comes to built in types like this, the standard does not guarantee that i will be incremented immediately after it's evaluation. The compiler is allowed to translate the above into:

cout << (i + i) << endl;i += 1;i += 1;


Which would print out a measly 2.
TEUTON
TEUTON
Amazing Explanation. Thanks A Ton.
haegarr
haegarr
So, e.g. an expression
* a ++
where both operators * and ++ have the same priority is evaluated as
* ( a ++ )
because for these operators the R-L associativity is specified. The operator associativity plays a role here since both operators are of same priority and in sequence (what in fact defines an order of evaluation). In the case
a = i++
the both evaluations of i are independent because the (distinctly prioritized) assignment operator seperates them, and hence
a[old_i] = old_i
a[old_i+1] = old_i
are the both possibilities!?
MaulingMonkey
MaulingMonkey
Quote:
Original post by haegarr
So, e.g. an expression
* a ++
where both operators * and ++ have the same priority is evaluated as
* ( a ++ )
because for these operators the R-L associativity is specified.


Correct.

Quote:
The operator associativity plays a role here since both operators are of same priority and in sequence (what in fact defines an order of evaluation).


Correct in this case, because *(...) needs the result of (a++).

Quote:
In the case
a = i++
the both evaluations of i are independent because the (distinctly prioritized) assignment operator seperates them,


My brain has trouble grokking it that way, but I think that works.

I would simply focus on the fact that, there's a binary operator (operator=). If either "argument" modifys something the other depends on, it enters the realm of the undefined. For a = i++, the problem is that i++ modifies i, something that a depends on - which means which "argument" is evaluated first matters (wheither a means a[old_i] or a[new_i]).

Quote:
and hence
a[old_i] = old_i
a[old_i+1] = old_i
are the both possibilities!?


Yes, with i=old_i+1 afterwards as well for both of course.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.