Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

i = v[i++] results in undefined behavior. Can't understand why.

59 views
Skip to first unread message

Armen Tsirunyan

unread,
Sep 26, 2010, 11:36:41 AM9/26/10
to
Please help me, I just can't understand this.
Clause 1.9 Paragraph 15 (n3092) says:

Except where noted, evaluations of operands of individual operators
and of subexpressions of individual
expressions are unsequenced. [ Note: In an expression that is
evaluated more than once during the execution
of a program, unsequenced and indeterminately sequenced evaluations of
its subexpressions need not be
performed consistently in different evaluations. —end note ] The value
computations of the operands of an
operator are sequenced before the value computation of the result of
the operator. If a side effect on a scalar
object is unsequenced relative to either another side effect on the
same scalar object or a value computation
using the value of the same scalar object, the behavior is undefined.
[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

let's consider i = v[i++]. the side effect of i being incremented by 1
is SEQUENCED before the side effect of i being assigned v[i++],
because "The value computations of the operands of an operator are
sequenced before the value computation of the result of the operator".
So how come is this undefined behavior?

Thank you in advance,
Armen Tsirunyan

Andreas Milton M

unread,
Sep 26, 2010, 12:58:48 PM9/26/10
to
Hello,

as far as I know a temporary object will be involved when the statement
is evaluated. Let's call it tmp. Semantically two different thing could
happen. See below.

First evaluation order
tmp := v[i] ; increment i ; i := tmp.

Second evalutaion order:
tmp := v[i] ; i:= tmp ; increment i;

In the first case we gain i+1 := v[i], whereas in the second case we
gain i = v[i+1].

Yours,
Andreas

Johannes Schaub (litb)

unread,
Sep 26, 2010, 1:00:59 PM9/26/10
to
Armen Tsirunyan wrote:

Because value computations do not include side effects. So you have two
unsequenced side effects in your snippet (the increment and assignment).
Moreover you have a value computation on i (left i) that is unsequenced
relative to a side effect on i (the right "i++").

If you write this as "i = v[++i]", which is equivalent to "i = *(v + (i = i
+ 1))" you will not have two unsequenced side effects anymore, because the
assignment in "i = i + 1" is sequenced before the assignment in "i = *(...".
BUT you still have the same value computation be unsequenced to the same
side effect as in your snippet. So for both the pre and postfix version you
have undefined behavior.

Andreas Milton M

unread,
Sep 26, 2010, 1:30:51 PM9/26/10
to
Hello,

As far as I know during the evaluation of "i = v[i++]" two semantically
different things could happen. It is easier to see that by replacing the
the =-operator by a pseudo-code function assign(x,y) which realizes x = y.

First evaluation of assign(i, v[i++]):

Evaluate i, then add v[i++] to i.
We gain i := 1 + v[i]

Second evaluation of assign(i, v[i++]):
Evaluate i++, then evaluate i, evaluate v[i]

The side-effect modifies i before we evaluate the index operator.
Then we gain i = v[i+1]

The order of expression evaluation is not uniquely determined. I think
this is what the phrase you cite from clause 1.9 paragraph 15 (n3092)
is about:

"Except where noted, evaluations of operands of individual operators
and of subexpressions of individual expressions are unsequenced. "

(unsequenced = order not uniquely specified)

Yours,
Andreas

Armen Tsirunyan

unread,
Sep 26, 2010, 1:38:53 PM9/26/10
to
On Sep 26, 10:00 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:

Thanks, I understand this now.

> Moreover you have a value computation on i (left i) that is unsequenced
> relative to a side effect on i (the right "i++").

But I don't understand this, is there a value computation of the left
i here? I mean an lvalue is expected here, which as I understand
means the object not the value. I mean this is rather an object
identification rather than value computation. Is there a more formal
definition of what a value computation is and what a side effect on an
object is?

Andreas Milton M

unread,
Sep 26, 2010, 1:39:19 PM9/26/10
to
Hello,

As far as I know during the evaluation of "i = v[i++]" two semantically
different things could happen. It is easier to see that by replacing the
the =-operator by a pseudo-code function assign(x,y) which realizes x = y.

First evaluation of assign(i, v[i++]):

Evaluate i, then associate v[i++] to i.


We gain i := 1 + v[i]

Second evaluation of assign(i, v[i++]):
Evaluate i++, then evaluate i, evaluate v[i]

The side-effect modifies i before we evaluate the index operator.
Then we gain i = v[i+1]

The order of expression evaluation is not uniquely determined. I think
this is what the phrase you cite from clause 1.9 paragraph 15 (n3092)
is about:

"Except where noted, evaluations of operands of individual operators


and of subexpressions of individual expressions are unsequenced. "

(unsequenced = order not uniquely specified)

Yours,
Andreas

On 09/26/2010 05:36 PM, Armen Tsirunyan wrote:

Armen Tsirunyan

unread,
Sep 26, 2010, 1:46:05 PM9/26/10
to
On Sep 26, 10:00 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:

If I may quote you from another thread :)
> >   ++i = 0; // defined by c++0x, undefined by C++03
> >   ++ ++i; // defined by c++0x, undefined by C++03
> >   i = ++i; // defined by c++0x, undefined by C++03
>
> Please disregard the last one. That's still undefined in C++0x it seems.
> Value computation of the left i is not sequenced relative to the side effect
> of "++i".

i = ++i; whether or not this is defined depends pretty much on what a
value computation means.
Also, is
(++i)++;
defined?
I guess not, am I right?

Johannes Schaub (litb)

unread,
Sep 26, 2010, 1:53:19 PM9/26/10
to
Armen Tsirunyan wrote:

At 1.9/12 of n3126 it says

"Evaluation of an expression (or a sub-expression) in general includes both
value computations (including determining the identity of an object for
glvalue evaluation and fetching a value previously assigned to an object for
prvalue evaluation) and initiation of side effects."

Johannes Schaub (litb)

unread,
Sep 26, 2010, 1:57:38 PM9/26/10
to
Armen Tsirunyan wrote:

++i is

i = i + 1

The Standard says for postfix i++:

"The value computation of the ++ expression is sequenced before the
modification of the operand object."

And it says for i = x

"In all cases, the assignment is sequenced after the value computation of
the right and left operands, and before the value computation of the
assignment expression."

Together this means that all side effects are sequenced and all side effects
are sequenced relative to all value computations. Thus the behavior of
"(++i)++" is defined.

Armen Tsirunyan

unread,
Sep 26, 2010, 1:59:07 PM9/26/10
to
On Sep 26, 10:53 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>

so, the incrementation of i in i++ is a side effect, whereas the
incrementation of i in ++i is a part of value computation, and
therefore makes ++++i defined. Is that correct?

Andreas Milton M

unread,
Sep 26, 2010, 1:59:12 PM9/26/10
to
Hello,

To my knowledge the above ++(i++) expression is absolutely well-defined
since the brackets enforce uniqueness.
C++ as any language guarantees that subexpressions of an expression are
evaluated before the expression itself is evaulated. Ambiguities only
arise in the order of the evaluations of subexpressions, which are
unsequenced (=unspecified) in C++ for good reason. But here the bracket
inforces uniqueness. The brackets can be understood as a unary operator.

We would get the following syntax tree:

pre-increment
|
brackets
|
post-increment
|
i

Where is the ambiguity? I don't see any, albeit the expression is ugly
of course. Even if we take out the brackets the expression would be
unique, since right increments have higher priority than left increments.

Cheers,
Andreas

Andreas Milton M

unread,
Sep 26, 2010, 2:00:59 PM9/26/10
to
Hello,

You say in your reply that ++(i++) shoould not be well-defined. I don't
think that's right. please see below.

On 09/26/2010 07:46 PM, Armen Tsirunyan wrote:

Armen Tsirunyan

unread,
Sep 26, 2010, 2:03:19 PM9/26/10
to
On Sep 26, 10:59 pm, Andreas Milton M <andreas.milto...@gmail.com>
wrote:

I wrote (++i)++, not ++(i++), which would be afaik ill-formed since i+
+ is a prvalue. Also I thought that parentheses do notcontribute to
the order of evaluation of operands or their sequencing, rather, they
specify the composition tree of the subexpressions in an expression. I
am so confused right now :)

Johannes Schaub (litb)

unread,
Sep 26, 2010, 2:06:08 PM9/26/10
to
Johannes Schaub (litb) wrote:

I suspect you will also need the one you quoted to really nail it down: "The

value computations of the operands of an operator are sequenced before the

value computation of the result of the operator.". So in the end, I think
the defined order is

glvalue evaluation of i
assignment to i
glvalue evaluation of i
prvalue evaluation of i
increment of i

Johannes Schaub (litb)

unread,
Sep 26, 2010, 2:09:22 PM9/26/10
to
Armen Tsirunyan wrote:

I agree with you on this.

Johannes Schaub (litb)

unread,
Sep 26, 2010, 2:18:06 PM9/26/10
to
Armen Tsirunyan wrote:

The increment is not part of value computation. But it is sequenced before a
value computation and after a value computation since ++i is equivalent to
i+=1 (which is equivalent to "i = i + 1" except that i is only evaluated
once as the lhs). So you have a sequence of

glvalue computation
increment
glvalue computation
increment
glvalue computation
....

For "++ ... ++ i;" .

Andreas Milton M

unread,
Sep 26, 2010, 2:30:03 PM9/26/10
to

I'm confused now too, so I'm not quite sure on what we agree or
disagree. But I'll try to restate my argument in a different shape so
that you can agree with it or find the mistake.

Any expression has a unique syntax tree. In order to evaluate one node
in the tree we need to evaluate all of its children first. The order by
which we evaluate the subexpressions is unspecified however.
A side-effect like i++ is not visible at the place where we evaluate the
expression i++ but at the next time we are confronted with the leaf i in
the syntax tree. If the next position where we meet i in the syntax tree
is uniquely specified by the structure of the tree, we have no ambiguity
problem, otherwise we do.

Let expr be an expression, then by the grammar of c++ the expression
(expr) is an expression too. There expression trees must be different,
otherwise expr and (expr) would be the same expression.
The expression tree is a subtree of (expr)

We have an expression tree for expr, call it T_expr.

The expression tree for (expr) is the tree
()
|
expr

You have three choices:
1) Find the mistake
2) accept it.
3) or, if it's giving you too much of a headache, like side-effects
always do, ignore it :)

Cheers,
Andreas

Andreas Milton M

unread,
Sep 26, 2010, 3:00:24 PM9/26/10
to
On 09/26/2010 08:09 PM, Johannes Schaub (litb) wrote:

You're right. I agree now too.

Bo Persson

unread,
Sep 26, 2010, 3:04:17 PM9/26/10
to
Armen Tsirunyan wrote:
> On Sep 26, 10:00 pm, "Johannes Schaub (litb)"
> <schaub-johan...@web.de> wrote:
>> Armen Tsirunyan wrote:
>>> Please help me, I just can't understand this.
>>> Clause 1.9 Paragraph 15 (n3092) says:
>>
>>> Except where noted, evaluations of operands of individual
>>> operators and of subexpressions of individual
>>> expressions are unsequenced. [ Note: In an expression that is
>>> evaluated more than once during the execution
>>> of a program, unsequenced and indeterminately sequenced
>>> evaluations of its subexpressions need not be
>>> performed consistently in different evaluations. 容nd note ] The

>>> value computations of the operands of an
>>> operator are sequenced before the value computation of the result
>>> of the operator. If a side effect on a scalar
>>> object is unsequenced relative to either another side effect on
>>> the same scalar object or a value computation
>>> using the value of the same scalar object, the behavior is
>>> undefined. [ Example:
>>> void f(int, int);
>>> void g(int i, int* v) {
>>> i = v[i++]; // the behavior is undefined
>>> i = 7, i++, i++; // i becomes 9
>>> i = i++ + 1; // the behavior is undefined
>>> i = i + 1; // the value of i is incremented
>>> f(i = -1, i = -1); // the behavior is undefined
>>> }
>>> 容nd example ]

>>
>>> let's consider i = v[i++]. the side effect of i being incremented
>>> by 1 is SEQUENCED before the side effect of i being assigned
>>> v[i++], because "The value computations of the operands of an
>>> operator are sequenced before the value computation of the result
>>> of the operator". So how come is this undefined behavior?
>>
>> Because value computations do not include side effects. So you
>> have two unsequenced side effects in your snippet (the increment
>> and assignment).
>
> Thanks, I understand this now.
>
>> Moreover you have a value computation on i (left i) that is
>> unsequenced relative to a side effect on i (the right "i++").
>
> But I don't understand this, is there a value computation of the
> left i here? I mean an lvalue is expected here, which as I
> understand means the object not the value. I mean this is rather an
> object identification rather than value computation. Is there a
> more formal definition of what a value computation is and what a
> side effect on an object is?

You have two side effects here, storing the result of i++ and storing
the result of the assignment to i. The fact that they are both
performed without an intervening sequence point is what causes the
undefined behavior.

My understanding is that the committee knows about hardware where this
actually doesn't work, so they defined the language accordingly.

Bo Persson


James Kanze

unread,
Sep 27, 2010, 11:09:17 AM9/27/10
to
On Sep 26, 6:57 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
> Armen Tsirunyan wrote:

[...]


> > i = ++i; whether or not this is defined depends pretty much
> > on what a value computation means.

It means what it says: the computation of the *value* of the
expression, without any side effects.

> > Also, is
> > (++i)++;
> > defined?
> > I guess not, am I right?

> ++i is

> i = i + 1

> The Standard says for postfix i++:

> "The value computation of the ++ expression is sequenced before the
> modification of the operand object."

> And it says for i = x

> "In all cases, the assignment is sequenced after the value
> computation of the right and left operands, and before the
> value computation of the assignment expression."

> Together this means that all side effects are sequenced and
> all side effects are sequenced relative to all value
> computations. Thus the behavior of "(++i)++" is defined.

Where do you get that from? The sentences you quote only speak
of sequencing the *value* computation. Nothing about side
effects.

--
James Kanze

James Kanze

unread,
Sep 27, 2010, 11:15:59 AM9/27/10
to
On Sep 26, 7:30 pm, Andreas Milton M <andreas.milto...@gmail.com>
wrote:

> On 09/26/2010 08:09 PM, Johannes Schaub (litb) wrote:

[...]


> Any expression has a unique syntax tree. In order to evaluate
> one node in the tree we need to evaluate all of its children
> first.

What does it mean to "evaluate" a child? The standard doesn't
say anything like this. What we do need is to "value compute"
the child nodes. There's no requirement for more.

> The order by which we evaluate the subexpressions is
> unspecified however. A side-effect like i++ is not visible at
> the place where we evaluate the expression i++ but at the next
> time we are confronted with the leaf i in the syntax tree.

When the side effect is visible depends on a lot of things.
Within a single thread, it is ordered with respect to the next
sequence point, and no more. And even then, we have to be
careful what we are talking about---the compiler may never write
the results of the incrementation, as long as the observable
behavior is the same as if it had (or there are some interthread
sequencing primitives which require it).

> If the next position where we meet i in the syntax tree is
> uniquely specified by the structure of the tree, we have no
> ambiguity problem, otherwise we do.

There's no ambiguity. You're just confusing the guarantees
concerning the results of an expression with the order side
effects occur; the two are largely unrelated.

> Let expr be an expression, then by the grammar of c++ the
> expression (expr) is an expression too. There expression trees
> must be different, otherwise expr and (expr) would be the same
> expression. The expression tree is a subtree of (expr)

Not in any of the compilers I've seen.

--
James Kanze

Johannes Schaub (litb)

unread,
Sep 27, 2010, 1:50:04 PM9/27/10
to
James Kanze wrote:

It says "The value computation of the ++ expression is sequenced before the
modification of the operand object". The latter is the side effect. So you
have the modification of "++i" before value computation of that expression,
and that value computation before value computation of the "<lval>++"
expression (because value computation of operands of an operator is
sequenced before value computation of their result), which in turn is
sequenced before modification of "<lval>".

So since "sequenced before" is a transitive relation, both side effects are
sequenced with respect to each other and with respect to value computations.

James Kanze

unread,
Sep 28, 2010, 3:34:26 AM9/28/10
to
On Sep 27, 6:50 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>

> >> ++i is

The passage you quote says just the opposite. And logically, it
couldn't be any other way: you can't have the side effect (the
modification of i) before having calculated its value.

> and that value computation before value computation of the
> "<lval>++" expression (because value computation of operands
> of an operator is sequenced before value computation of their
> result), which in turn is sequenced before modification of
> "<lval>".

> So since "sequenced before" is a transitive relation, both
> side effects are sequenced with respect to each other and with
> respect to value computations.

Where do you see any sequencing of the two side effects?

--
James Kanze

litb

unread,
Sep 28, 2010, 7:03:05 AM9/28/10
to

No. It says that the modification in "i++" happens after value
computation of "i++". What I referred to is the modification in "++i"
to be sequenced before value computation of "++i". I should probably
have started another paragraph to make that clear, but it seemed
obvious to me.

> > and that value computation before value computation of the
> > "<lval>++" expression (because value computation of operands
> > of an operator is sequenced before value computation of their
> > result), which in turn is sequenced before modification of
> > "<lval>".
> > So since "sequenced before" is a transitive relation, both
> > side effects are sequenced with respect to each other and with
> > respect to value computations.
>
> Where do you see any sequencing of the two side effects?
>

I see it in my above description. Please show us where you don't see
it. I.e where the sequence breaks for you, so we can work it out.
Otherwise, I'm just going to copy/paste the above description again.

James Kanze

unread,
Sep 28, 2010, 1:15:49 PM9/28/10
to

In the expression "i++", the side effect is sequenced after the
value computation. Which is in direct contradiction to the last
sentence of yours to which I was responding.

> What I referred to is the modification in "++i"
> to be sequenced before value computation of "++i".

The side effects of "++i" are not sequenced before anything in
any of the expressions in this thread.

> I should probably have started another paragraph to make that
> clear, but it seemed obvious to me.

> > > and that value computation before value computation of the
> > > "<lval>++" expression (because value computation of operands
> > > of an operator is sequenced before value computation of their
> > > result), which in turn is sequenced before modification of
> > > "<lval>".
> > > So since "sequenced before" is a transitive relation, both
> > > side effects are sequenced with respect to each other and with
> > > respect to value computations.

> > Where do you see any sequencing of the two side effects?

> I see it in my above description.

Yes, but on what do you base your description? Where is
there text in the standard supporting it? (There may be,
but I've not seen it.) Where do you find anything
concerning the sequencing of side effects (as opposed to the
value computation) of "i++"? (The only thing I find is the
very generic text in §1.9/14-15, which guarantees that the
side effects are sequenced before the end of the full
expression and before function calls. There's additional
text in §5.14, §5.15, §5.16 and §5.18 which guarantees the
sequencing of side effects where the &&, ||, ?: and comma
operator are involved. But that's all I find. (In fact,
the places where side effects are sequenced seem to
correspond exactly to the places where there are sequence
points in C++03. This isn't by accident; the intent is that
in a single threaded environment, the rules don't change.)

> Please show us where you don't see it. I.e where the
> sequence breaks for you, so we can work it out.
> Otherwise, I'm just going to copy/paste the above
> description again.

Your "description" is a vacuous claim. I'm asking about
evidence. As far as I can tell, the current rules do not
change anything with regards to what is or is not legal in
a single threaded program. (They do make it a lot clearer.)

--
James Kanze

James Kanze

unread,
Sep 28, 2010, 1:20:03 PM9/28/10
to
On Sep 26, 6:59 pm, Armen Tsirunyan <lordn3m...@gmail.com> wrote:
> On Sep 26, 10:53 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
> wrote:

I'm going back in this thread, because this seems to be
where the error was introduced.

[...]


> > >> Moreover you have a value computation on i (left i) that is unsequenced
> > >> relative to a side effect on i (the right "i++").
>
> > > But I don't understand this, is there a value computation of the left
> > > i here? I mean an lvalue is expected here, which as I understand
> > > means the object not the value. I mean this is rather an object
> > > identification rather than value computation. Is there a more formal
> > > definition of what a value computation is and what a side effect on an
> > > object is?

> > At 1.9/12 of n3126 it says

> > "Evaluation of an expression (or a sub-expression) in general includes both
> > value computations (including determining the identity of an object for
> > glvalue evaluation and fetching a value previously assigned to an object for
> > prvalue evaluation) and initiation of side effects."

> so, the incrementation of i in i++ is a side effect, whereas the
> incrementation of i in ++i is a part of value computation, and
> therefore makes ++++i defined. Is that correct?

No. The modification of i, whether in i++ or ++i, is a side
effect, not a value computation. The value computation of
++i is the determination of the object i and the
caluculation resulting from adding one to its current value.
The value computation does *not* include any modification of
memory. Ever.

--
James Kanze

Johannes Schaub (litb)

unread,
Sep 28, 2010, 4:00:01 PM9/28/10
to
James Kanze wrote:

I can only repeat myself. I talked about ++i, and you still talk about i++.

>> What I referred to is the modification in "++i"
>> to be sequenced before value computation of "++i".
>
> The side effects of "++i" are not sequenced before anything in
> any of the expressions in this thread.
>

You missed what I quoted from the Standard, it seems:

"In all cases, the assignment is sequenced after the value computation of
the right and left operands, and before the value computation of the
assignment expression."

That assignment is the one in "x+=1", which is the expression that "++x" is
equivalent to.

>> I should probably have started another paragraph to make that
>> clear, but it seemed obvious to me.
>
>> > > and that value computation before value computation of the
>> > > "<lval>++" expression (because value computation of operands
>> > > of an operator is sequenced before value computation of their
>> > > result), which in turn is sequenced before modification of
>> > > "<lval>".
>> > > So since "sequenced before" is a transitive relation, both
>> > > side effects are sequenced with respect to each other and with
>> > > respect to value computations.
>
>> > Where do you see any sequencing of the two side effects?
>
>> I see it in my above description.
>
> Yes, but on what do you base your description? Where is
> there text in the standard supporting it? (There may be,
> but I've not seen it.)

I don't know how to make it clearer.

>
>> Please show us where you don't see it. I.e where the
>> sequence breaks for you, so we can work it out.
>> Otherwise, I'm just going to copy/paste the above
>> description again.
>
> Your "description" is a vacuous claim. I'm asking about
> evidence. As far as I can tell, the current rules do not
> change anything with regards to what is or is not legal in
> a single threaded program. (They do make it a lot clearer.)
>

I think my description is clear and the Standard's description is also
clear. Well, I gave not only evidence (as in "try it on GCC and see"), but I
gave a complete proof (as in "work it out by the Standard's text") :)

Pete Becker

unread,
Sep 28, 2010, 4:06:04 PM9/28/10
to
On 2010-09-28 13:15:49 -0400, James Kanze said:

>
> As far as I can tell, the current rules do not
> change anything with regards to what is or is not legal in
> a single threaded program. (They do make it a lot clearer.)

That's good, because that's what was intended.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Johannes Schaub (litb)

unread,
Sep 28, 2010, 5:04:30 PM9/28/10
to
Pete Becker wrote:

> On 2010-09-28 13:15:49 -0400, James Kanze said:
>
>>
>> As far as I can tell, the current rules do not
>> change anything with regards to what is or is not legal in
>> a single threaded program. (They do make it a lot clearer.)
>
> That's good, because that's what was intended.
>

Does that mean that you agree with James that "++ ++ i" and "(++i)++" are
still undefined in C++0x (like they were in C++03)? How does that follow
from the rules in n3126?

I actually find it stating quite the opposite, and all people I explained
the rules to agree with me so far (and I also have "evidence" of GCC4.6),
except James does not see or does not want to see that.

We have very knowledgable reading this, including you as the Standards
editor and Anthony Williams. It shouldn't be too difficult to find out.

Kai-Uwe Bux

unread,
Sep 28, 2010, 5:50:11 PM9/28/10
to
Johannes Schaub (litb) wrote:

Ok, let's consider

( x += 1 ) ++

I can see 6 events:

a) value computation of ( x += 1 ) ++
b) side effect of _ ++
c) value computation of x += 1
d) side effect of _ += _
e) value computation of x
f) value computation of 1

Let x | y denote "x is sequenced before y". From [5.17/1], I gather that

e,f | d | c

and from [5.2.6/1] that

a | b


What I cannot deduce is c | a, i.e., that a value has to be computed before
it can be incremented. Where would I find that?


Best

Kai-Uwe Bux

Johannes Schaub (litb)

unread,
Sep 28, 2010, 6:39:10 PM9/28/10
to
Kai-Uwe Bux wrote:

1.9/15: "The value computations of the operands of an operator are sequenced

Kai-Uwe Bux

unread,
Sep 28, 2010, 6:40:04 PM9/28/10
to
Johannes Schaub (litb) wrote:

Thanks.


Best

Kai-Uwe Bux

James Kanze

unread,
Sep 29, 2010, 4:28:23 AM9/29/10
to
On Sep 28, 9:00 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>

wrote:
> James Kanze wrote:
> > On Sep 28, 12:03 pm, litb <litb...@googlemail.com> wrote:

> >> > The passage you quote says just the opposite. And logically, it
> >> > couldn't be any other way: you can't have the side effect (the
> >> > modification of i) before having calculated its value.

> >> No. It says that the modification in "i++" happens after value
> >> computation of "i++".

> > In the expression "i++", the side effect is sequenced after the
> > value computation. Which is in direct contradiction to the last
> > sentence of yours to which I was responding.

> I can only repeat myself. I talked about ++i, and you
> still talk about i++.

That's a typo on my part, but it doesn't change anything.
Both behave identically in this respect.

> >> What I referred to is the modification in "++i"
> >> to be sequenced before value computation of "++i".

> > The side effects of "++i" are not sequenced before anything in
> > any of the expressions in this thread.

> You missed what I quoted from the Standard, it seems:

> "In all cases, the assignment is sequenced after the value
> computation of the right and left operands, and before the
> value computation of the assignment expression."

No, I didn't miss it. It's just not relevant, since we're
talking about the sequencing of side effects, not value
computation.

> That assignment is the one in "x+=1", which is the
> expression that "++x" is equivalent to.

> >> I should probably have started another paragraph to make that
> >> clear, but it seemed obvious to me.

> >> > > and that value computation before value computation of the
> >> > > "<lval>++" expression (because value computation of operands
> >> > > of an operator is sequenced before value computation of their
> >> > > result), which in turn is sequenced before modification of
> >> > > "<lval>".
> >> > > So since "sequenced before" is a transitive relation, both
> >> > > side effects are sequenced with respect to each other and with
> >> > > respect to value computations.

> >> > Where do you see any sequencing of the two side effects?

> >> I see it in my above description.

> > Yes, but on what do you base your description? Where is
> > there text in the standard supporting it? (There may be,
> > but I've not seen it.)

> I don't know how to make it clearer.

By quoting some relevant passage in the standard, rather
than just restating the same thing, without justification.

> >> Please show us where you don't see it. I.e where the
> >> sequence breaks for you, so we can work it out.
> >> Otherwise, I'm just going to copy/paste the above
> >> description again.

> > Your "description" is a vacuous claim. I'm asking about
> > evidence. As far as I can tell, the current rules do not
> > change anything with regards to what is or is not legal in
> > a single threaded program. (They do make it a lot clearer.)

> I think my description is clear and the Standard's
> description is also clear.

Quite. It's also clear that they say different things, that
your description doesn't correspond to the standard.

> Well, I gave not only evidence (as in "try it on GCC and
> see"), but I gave a complete proof (as in "work it out by
> the Standard's text") :)

You've yet to quote any text concerning the sequencing of
side effects in this case. All of the text you cite
concerning value computation.

--
James Kanze

James Kanze

unread,
Sep 29, 2010, 4:39:25 AM9/29/10
to
On Sep 28, 10:50 pm, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
> Johannes Schaub (litb) wrote:
> > James Kanze wrote:

[...]
> Ok, let's consider

> ( x += 1 ) ++

> I can see 6 events:

> a) value computation of ( x += 1 ) ++
> b) side effect of _ ++
> c) value computation of x += 1
> d) side effect of _ += _
> e) value computation of x
> f) value computation of 1

> Let x | y denote "x is sequenced before y". From [5.17/1], I gather that

> e,f | d | c

> and from [5.2.6/1] that

> a | b

> What I cannot deduce is c | a, i.e., that a value has to
> be computed before it can be incremented. Where would
> I find that?

§1.9/15.

But that's not the point. The original question is whether
such an expression has undefined behavior. In §1.9/15, we
have "If a side effect on a scalar object is unsequenced


relative to either another side effect on the same scalar
object or a value computation using the value of the same

scalar object, the behavior is undefined." In the given
expression, we have two side effects on x, b and d. They
are not sequenced, since "Except where noted, evaluations of


operands of individual operators and of subexpressions of

individual expressions are unsequenced" (§1.9/15), and no
one has posted anything in from the standard which says
otherwise (the "except where noted"). Unless either
b | d or d | b, the expression has undefined behavior.

QED

--
James Kanze

James Kanze

unread,
Sep 29, 2010, 4:46:54 AM9/29/10
to
On Sep 28, 10:04 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:

> Pete Becker wrote:
> > On 2010-09-28 13:15:49 -0400, James Kanze said:

> >> As far as I can tell, the current rules do not
> >> change anything with regards to what is or is not legal in
> >> a single threaded program. (They do make it a lot clearer.)

> > That's good, because that's what was intended.

> Does that mean that you agree with James that "++ ++ i"
> and "(++i)++" are still undefined in C++0x (like they were
> in C++03)? How does that follow from the rules in n3126?

> I actually find it stating quite the opposite,

But you've yet to post the passage on which you base your
statement.

The standard is a large document, and it's quite possible
that I've missed something in it. My argument is based on
a sentence which starts "Except where noted", and it's quite
possible that something is noted somewhere that I've missed.
All I'm asking is that someone post it.

> and all
> people I explained the rules to agree with me so far (and
> I also have "evidence" of GCC4.6), except James does not
> see or does not want to see that.

I don't see how anything a compiler does can "prove" that
the standard doesn't specify undefined behavior. In this
case, I'm fairly sure that the intent was that the behavior
be undefined (and Pete has just backed me up there). If the
wording actually corresponds to the intent (and I've yet to
see any passage in the standard which says otherwise), then
whatever G++ does is conformant.

--
James Kanze

Kai-Uwe Bux

unread,
Sep 29, 2010, 4:49:52 AM9/29/10
to
James Kanze wrote:

So, what about [1.9/13]:

Sequenced before is an asymmetric, transitive, pair-wise relation between
evaluations executed by a single thread (1.10), which induces a partial
order among those evaluations ...

The operative word being "transitive".

Since d | c, c | a, and a | b, transitivity implies d | b.


Best

Kai-Uwe Bux

litb

unread,
Sep 29, 2010, 6:59:47 AM9/29/10
to

For getting evidence, I abused GCCs "this may be undefined" warning.
It does not emit such a warning for "++ ++ i" and "(++i)++". :)

Armen Tsirunyan

unread,
Sep 29, 2010, 7:24:18 AM9/29/10
to

but did it emit such a warning in C++2003 mode? I mean those two were
definitely undefined there, weren't they?

James Kanze

unread,
Sep 29, 2010, 12:21:21 PM9/29/10
to

> >> e,f | d | c

> >> and from [5.2.6/1] that

> >> a | b

> > §1.9/15.

> > QED

> So, what about [1.9/13]:

You may have something there. The critical phrase is "In


all cases, the assignment is sequenced after the value
computation of the right and left operands, and before the

value computation of the assignment expression." Although
I'm not sure what is meant by the first "the
assignment"---from a normal English point of view, I'd say
the side effects, but I've been caught out by standardese
before. If that is really what is meant (and it's hard to
find any other interpretation), it's new. (In C++03 terms,
it's roughly the equivalent of making the assignment
operator a sequence point.) I doubt that it is what was
meant, but I think the standard does need some
clarification: in most (all) other cases, when specifying
the sequencing of (sub-)expressions, the standard states
explicitly whether it is the value calculation or the side
effects which are sequenced.

--
James Kanze

Alf P. Steinbach /Usenet

unread,
Sep 29, 2010, 12:47:04 PM9/29/10
to
* James Kanze, on 29.09.2010 18:21:

Uhm, someone asked me about this issue privately, and I had to say that I knew
next to nothing about C++0x rules and couldn't provide any insight.

What I should have said was what's occurring to me now:

It does not matter what corner cases C++0x makes well-defined.

One has to code as if by (well known) C++98 rules anyway.

Don't you agree?


Cheers,

- Alf

--
blog at <url: http://alfps.wordpress.com>

Kai-Uwe Bux

unread,
Sep 29, 2010, 1:11:24 PM9/29/10
to
James Kanze wrote:

Agreed. Whatever is meant by "the assignment", it is clear that there are
four things being sequenced, "the assignment" being wedged after two and
before the last. The other three are value computations. If "the assignment"
is not the side effect, I would definitely like to know what it is.

> If that is really what is meant (and it's hard to
> find any other interpretation), it's new. (In C++03 terms,
> it's roughly the equivalent of making the assignment
> operator a sequence point.)

True.

> I doubt that it is what was
> meant, but I think the standard does need some
> clarification: in most (all) other cases, when specifying
> the sequencing of (sub-)expressions, the standard states
> explicitly whether it is the value calculation or the side
> effects which are sequenced.

I am not so sure that the side-effect could not be meant. I have no
experience in multi-threading, but maybe there are code snippets
demonstrating the need of such sequencing. Are discussions of the committee
available online where one could find examples and rationales?


Best

Kai-Uwe Bux

James Kanze

unread,
Sep 30, 2010, 5:41:08 AM9/30/10
to
On Sep 29, 5:47 pm, "Alf P. Steinbach /Usenet" <alf.p.steinbach

+use...@gmail.com> wrote:
> * James Kanze, on 29.09.2010 18:21:

[...]


> Uhm, someone asked me about this issue privately, and
> I had to say that I knew next to nothing about C++0x rules
> and couldn't provide any insight.

> What I should have said was what's occurring to me now:

> It does not matter what corner cases C++0x makes well-defined.

> One has to code as if by (well known) C++98 rules anyway.

> Don't you agree?

Definitely. For a long time yet, anyway.

--
James Kanze

James Kanze

unread,
Sep 30, 2010, 5:51:24 AM9/30/10
to
On Sep 29, 6:11 pm, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
> James Kanze wrote:
> > On Sep 29, 9:49 am, Kai-Uwe Bux <jkherci...@gmx.net> wrote:

[...]


> >> Since d | c, c | a, and a | b, transitivity implies d | b.

> > You may have something there. The critical phrase is "In
> > all cases, the assignment is sequenced after the value
> > computation of the right and left operands, and before the
> > value computation of the assignment expression." Although
> > I'm not sure what is meant by the first "the
> > assignment"---from a normal English point of view, I'd say
> > the side effects, but I've been caught out by standardese
> > before.

> Agreed. Whatever is meant by "the assignment", it is clear that there are
> four things being sequenced, "the assignment" being wedged after two and
> before the last. The other three are value computations. If "the assignment"
> is not the side effect, I would definitely like to know what it is.

That's what I couldn't figure out. Taken alone, "the
assignment" really isn't very precise. In context, just
about everything but the side effect has been explicitly
mentionned, so there's nothing else left for it to be. (But
as I said, I've been caught out by standardese before.)

> > If that is really what is meant (and it's hard to
> > find any other interpretation), it's new. (In C++03 terms,
> > it's roughly the equivalent of making the assignment
> > operator a sequence point.)

> True.

> > I doubt that it is what was
> > meant, but I think the standard does need some
> > clarification: in most (all) other cases, when specifying
> > the sequencing of (sub-)expressions, the standard states
> > explicitly whether it is the value calculation or the side
> > effects which are sequenced.

> I am not so sure that the side-effect could not be meant.

Sloppy wording on my part. It didn't mean to use meant to
refer to the semantics of the phrase, but to the intention
behind it. I doubt that this is what was intended. (I.e.
we didn't mean to say what we said.) As Pete said, the
intention was that the actual effect of the rules didn't
change for single threaded programs: what was well defined
before remains well defined, and what was undefined before
remains undefined.

> I have no experience in multi-threading, but maybe there
> are code snippets demonstrating the need of such
> sequencing. Are discussions of the committee available
> online where one could find examples and rationales?

The discussions in the meetings, no, since they're purely
oral. The discussions on the reflectors are, but only to
"members". And I don't think that they're effectively
searchable, so it could take a lot of time to find what
you're looking for.

We probably should ask on comp.std.c++. We're more likely
to encounter someone who participated in the discussions
about this rewording there.

--
James Kanze

Johannes Schaub (litb)

unread,
Oct 9, 2010, 5:05:30 PM10/9/10
to
Armen Tsirunyan wrote:

> On Sep 26, 10:00 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>


> wrote:
>> Armen Tsirunyan wrote:
>> > Please help me, I just can't understand this.
>> > Clause 1.9 Paragraph 15 (n3092) says:
>>

>> > Except where noted, evaluations of operands of individual operators
>> > and of subexpressions of individual

>> > expressions are unsequenced. [ Note: In an expression that is
>> > evaluated more than once during the execution
>> > of a program, unsequenced and indeterminately sequenced evaluations of
>> > its subexpressions need not be

>> > performed consistently in different evaluations. —end note ] The value


>> > computations of the operands of an

>> > operator are sequenced before the value computation of the result of
>> > the operator. If a side effect on a scalar


>> > object is unsequenced relative to either another side effect on the
>> > same scalar object or a value computation
>> > using the value of the same scalar object, the behavior is undefined.

>> > [ Example:
>> > void f(int, int);
>> > void g(int i, int* v) {
>> > i = v[i++]; // the behavior is undefined
>> > i = 7, i++, i++; // i becomes 9
>> > i = i++ + 1; // the behavior is undefined
>> > i = i + 1; // the value of i is incremented
>> > f(i = -1, i = -1); // the behavior is undefined
>> > }

>> > —end example ]


>>
>> > let's consider i = v[i++]. the side effect of i being incremented by 1
>> > is SEQUENCED before the side effect of i being assigned v[i++],

>> > because "The value computations of the operands of an operator are


>> > sequenced before the value computation of the result of the operator".
>> > So how come is this undefined behavior?
>>
>> Because value computations do not include side effects. So you have two
>> unsequenced side effects in your snippet (the increment and assignment).

>> Moreover you have a value computation on i (left i) that is unsequenced
>> relative to a side effect on i (the right "i++").
>>

>> If you write this as "i = v[++i]", which is equivalent to "i = *(v + (i =
>> i + 1))" you will not have two unsequenced side effects anymore, because
>> the assignment in "i = i + 1" is sequenced before the assignment in "i =
>> *(...". BUT you still have the same value computation be unsequenced to
>> the same side effect as in your snippet. So for both the pre and postfix
>> version you have undefined behavior.
>
> If I may quote you from another thread :)
>> > ++i = 0; // defined by c++0x, undefined by C++03
>> > ++ ++i; // defined by c++0x, undefined by C++03
>> > i = ++i; // defined by c++0x, undefined by C++03
>>
>> Please disregard the last one. That's still undefined in C++0x it seems.
>> Value computation of the left i is not sequenced relative to the side
>> effect of "++i".


>
> i = ++i; whether or not this is defined depends pretty much on what a
> value computation means.

In fact, we had this case of "i = ++i" on stackoverflow now. The Standard
says "If a side effect on a scalar object is unsequenced relative to either

another side effect on the same scalar object or a value computation using

the value of the same scalar object, the behavior is undefined.". The key
point is "using the value of the same scalar object". It did not appear to
me until someone actually pointed it out to me.

We found that since an lvalue that just refers to an object does not use the
value of an object, that "i = ++i" is not UB. While "i" on the left side is
a value computation of that lvalue expression "i", it does not use the value
of the scalar object i. So we have "value computation" that acts on
expressions, but does not use the value of the object that it computed an
lvalue to, in case of glvalue evaluation. I suspect that this is a
reasonable reading of the Standard.

Bo Persson

unread,
Oct 10, 2010, 3:46:20 AM10/10/10
to
Johannes Schaub (litb) wrote:

> Armen Tsirunyan wrote:
>>
>> i = ++i; whether or not this is defined depends pretty much on
>> what a value computation means.
>
> In fact, we had this case of "i = ++i" on stackoverflow now. The
> Standard says "If a side effect on a scalar object is unsequenced
> relative to either another side effect on the same scalar object or
> a value computation using the value of the same scalar object, the
> behavior is undefined.". The key point is "using the value of the
> same scalar object". It did not appear to me until someone actually
> pointed it out to me.
>

No, the key point here is "another side effect on the same scalar
object". The increment and the assignment operator both have side
effects on i (storing new values).


Bo Persson


Johannes Schaub (litb)

unread,
Oct 10, 2010, 7:07:26 AM10/10/10
to
Bo Persson wrote:

No, this is from a feature Standard you have not yet read. It's not C++03.

Have a good day.

Johannes Schaub (litb)

unread,
Oct 10, 2010, 9:26:17 AM10/10/10
to
Pete Becker wrote:

> On 2010-09-28 13:15:49 -0400, James Kanze said:
>
>>
>> As far as I can tell, the current rules do not
>> change anything with regards to what is or is not legal in
>> a single threaded program. (They do make it a lot clearer.)
>
> That's good, because that's what was intended.
>

I have noticed that C++0x has changed one example for C++03's "undefined
behavior rule" from

i = ++i + 1; // C++03

to

i = i++ + 1; // C++0x

Using the analysis applied in this thread, we found that the wording
(anadvertedly?) makes the first well-defined. The second is still undefined.

What was the rationale to change that example from C++03?

Bo Persson

unread,
Oct 10, 2010, 9:38:42 AM10/10/10
to

I have read it :-), but anyway the original text is

"Between the previous and next sequence point a scalar object shall
have its stored value modified at most once by the evaluation
of an expression."

which makes no difference for single threaded execution.


Bo Persson


Johannes Schaub (litb)

unread,
Oct 10, 2010, 9:55:31 AM10/10/10
to
Johannes Schaub (litb) wrote:

Subsequently, I have looked up the reason myself. DR #637 and DR #222 show
that the examles shown are intended to be well defined in C++0x and
undefined in C++03.

I'm glad that I have sorted this out before doing an issue report to WMM.

Now, let's wait for "James Kanze" to tell me how deeply wrong I am about any
and everything :)

jl_...@hotmail.com

unread,
Oct 12, 2010, 7:30:55 PM10/12/10
to
On Sep 26, 9:36 am, Armen Tsirunyan <lordn3m...@gmail.com> wrote:
>
> let's consider i = v[i++]. the side effect of i being incremented by 1
> is SEQUENCED before the side effect of i being assigned v[i++],
> because "The value computations of the operands of an operator are
> sequenced before the value computation of the result of the operator".
> So how come is this undefined behavior?


I know I'm a latecomer to this thread, but maybe these explanations
will help.

========================

A C++ compiler is perfectly free to re-interpret a pre-increment
operation like this:

int a = ++i;

as:

i += 1;
int a = i;

========================

Likewise, a C++ compiler is perfectly free to re-interpret a post-
increment operation like this:

int a = i++;

as:

int temp = i; // a copy is made
i += 1; // the original is incremented
int a = temp; // the copy is assigned

or as:

int a = i;
i += 1; // no copy is made

Both are valid, and the second has the advantage that it is not any
less efficient than the pre-increment (which goes against the popular
belief that post-increment is necessarily less efficient than pre-
increment).

========================

In the same manner,

i = v[i++]; // undefined!

can be re-interpreted as:

int temp = i;
i += 1;
i = v[temp];

(which results in what you'd expect)

or as:

i = v[i];
i += 1;

(which is certainly not what you'd expect)

========================

Also in the same manner:

// (Let's assume i equals 1 here.)

int a = i++ + i++; // undefined!

can be re-interpreted as:

int a = i + i; // a would now be 2
i += 1;
i += 1;

or as:

int temp1 = i;
i += 1;
int temp2 = i;
i += 1;
int a = temp1 + temp2; // a would now be 3

========================

All of these re-interpretations are allowable to C++ compilers. But
as you can see, they don't always yield the same results in cases
where the variable is read twice in an expression where it's also
written to. Therefore, in those cases you get undefined behavior.

I hope this clears things up, Armen.

-- Jean-Luc


========================


P.S. Here I'll put a disclaimer:

According Bjarne Stroustrup's FAQ at http://www2.research.att.com/~bs/bs_faq2.html#evaluation-order
, these lines:

v[i] = i++;

and:

f(v[i], i++);

are undefined. However, it says nothing of the following line:

i = v[i++];

so I'm not positive that's undefined. It says that "if you read a
variable twice in an expression where you also write it, the result is
undefined."

But in "i = v[i++];", is the "i" variable read twice? I'm not sure.
If it is, then the result is undefined, but if it isn't, then the
result should be defined. I have to admit that I really don't know
about this one.

Bo Persson

unread,
Oct 13, 2010, 12:25:55 PM10/13/10
to

The belief is that pre-increment is at least as good as
post-increment. Therefore it should be the default, if there is no
other reason to select either.

>
>
> P.S. Here I'll put a disclaimer:
>
> According Bjarne Stroustrup's FAQ at
> http://www2.research.att.com/~bs/bs_faq2.html#evaluation-order
> , these lines:
>
> v[i] = i++;
>
> and:
>
> f(v[i], i++);
>
> are undefined. However, it says nothing of the following line:
>
> i = v[i++];
>
> so I'm not positive that's undefined. It says that "if you read a
> variable twice in an expression where you also write it, the result
> is
> undefined."

It also could say that you cannot write to a variable twice without an
intervening sequence point.


Bo Persson


jl_...@hotmail.com

unread,
Oct 13, 2010, 4:17:59 PM10/13/10
to

On Oct 13, 10:25 am, "Bo Persson" <b...@gmb.dk> wrote:
>
> The belief is that pre-increment is at least as good as
> post-increment. Therefore it should be the default, if
> there is no other reason to select either.


While I definitely belive that that is a common belief, I have to
disagree with it. According to some, postincrement is faster than
preincrement on certain platforms. (Someone on
http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=171881
makes this claim for the Motorola 68000.)

I think the reason this belief is so widespread is because most
programmers, if writing their own implementations, would create a
temporary copy for postincrement, while they would not bother with a
copy for preincrement. But in fact, there is nothing to say that
postincrementing a C integer (such as int, char, short, etc.) requires
a copy to be made, and conversely, there is no guarantee that
predecrementing can't make use of a temporary copy, or even several.

So while in theory pre-increment is at least as good as post-
increment, I have to disagree with it in practice. In practice, post-
increment can be as efficient, more efficient, or less efficient than
pre-increment. And the argument that one is at least as good as the
other because "that's the way I would do it" (or the mistaken claim
that "there is no way to do it without a temporary copy") isn't a good
one considering that there are platforms already in existence that go
contrary to this common belief. (I'm not saying you're using that
argument; but I am saying that I've heard it more than once from other
programmers as a way to avoid using post-increments altogether.)

In my opinion, i++ and ++i both have the same run-time behavior (in
big-O notation, they would both be O(1)), so it really doesn't matter
which one you use when measuring efficiency. A run-time bottleneck
won't ever disappear by replacing i++ with ++i (and vice-versa).
(Although I suppose it would be possible to see a difference if you
had a program that all it did was pre- or post-increment billions of
times and did absolutely nothing else (which I couldn't say would ever
be a very useful program). And even if you did, it's possible that
the post-increment version would be slightly more efficient than the
pre-increment version!)

You can probably tell that I've given a lot of thought about this,
and have defended myself against those who believe that "good
programmers never use post-increment." In practice, I think one
should use whichever is the one the situation is most suited for
(which I think you hold a similar position to), and ignore the
negligible efficiency difference. And when an integer is incremented
on a line all by itself (without it being assigned to anything), I
believe it doesn't matter at all whether you use i++ or ++i (in fact,
all the compilers I've tested that on compile both to the same
executable code, and even if they didn't, the run-time difference is
virtually nonexistent).


> It also could say that you cannot write to a variable
> twice without an intervening sequence point.

Yeah. Or even: In any line of code (defined as code delimited by
';', '&&', '||', or the comma operator), any variable that is pre- or
post- incremented/decremented should never be used more than once.
Otherwise, the result is undefined, and your refridgerator might
defrost. ;)

Yours,

-- Jean-Luc

0 new messages