postfix increment and assignment

39 views
Skip to first unread message

Josh Sebastian

unread,
Aug 3, 2000, 3:00:00 AM8/3/00
to
OK, I tried this code on MSVC6:

#include<iostream>
using namespace std;

int main()
{
int x = 0;
x = x++;
cout << x << endl;
}

The output was:
1

Now, I KNOW for a FACT that MSVC is not 100% conformant to the C++
standard, so I tend to trust my intuition and logic over the results
MSVC gives me (for example, MSVC gives me a warning about main not
returning a value =) But in this case I'm really not sure.
It seems to me that the x++ occurrs first, as the postfix increment
operator is higher in precedence than the assignment operator. Thus
the value at memory location is 1, but the result of the expression
x++ is 0 (the previous value of x). Then this value (0) should be
assigned to the memory location identified by x, overwriting the
effect of the increment. So, I should be getting 0. Is my
logic/understanding flawed?

I also tried writing:
int x = 0;
cout << x++ << endl << x << end;

and I got
0
1

Then I wrote a little class
class Number
{
int n;

public:
Number(int num=0) : n(num) { }
Number& operator++() { ++n; return *this; }
Number operator++(int) { Number prev = *this; ++n; return
prev; }
friend ostream& operator<<(ostream& os, const Number& num);
};

ostream& operator<<(ostream& os, const Number& num)
{ return os << num.n; }

When I replaced Number for int in my first program, ie:

int main()
{
Number n = 0;
n = n++;
cout << n << endl;
}

I got
0

So that worked like I thought it would. Now, naturally, I DEFINED the
postfix increment operator to work like I thought it should, but isn't
this the *correct* way to write it?

I can't back this up with a solid argument (mostly because I have a
very weak understanding of sequence points), but I feel that this is
related to the (a = b) = c problem that has been discussed recently,
bucause both statements involve two modifications of a single memory
location without (I think) a sequence point..

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


Robert O'Dowd

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Josh Sebastian wrote:
>
> OK, I tried this code on MSVC6:
>
> #include<iostream>
> using namespace std;
>
> int main()
> {
> int x = 0;
> x = x++;
> cout << x << endl;
> }
>
> The output was:
> 1
>
> Now, I KNOW for a FACT that MSVC is not 100% conformant to the C++
> standard, so I tend to trust my intuition and logic over the results
> MSVC gives me (for example, MSVC gives me a warning about main not
> returning a value =) But in this case I'm really not sure.
> It seems to me that the x++ occurrs first, as the postfix increment
> operator is higher in precedence than the assignment operator. Thus
> the value at memory location is 1, but the result of the expression
> x++ is 0 (the previous value of x). Then this value (0) should be
> assigned to the memory location identified by x, overwriting the
> effect of the increment. So, I should be getting 0. Is my
> logic/understanding flawed?

Unfortunately, your logic is flawed. You have introduced undefined
(or unspecified) behaviour.

The code above introduces undefined/unspecified behaviour because x is
modified twice between two sequence points (in the code
above "sequence points" are indicated by semi-colons).

>
> I also tried writing:
> int x = 0;
> cout << x++ << endl << x << end;
>
> and I got
> 0
> 1

With this one, the results are also undefined. You
could equally well get 0 both times. The reason is that
the expression x++ is not guaranteed to be evaluated
before x.

The cout << line could have the effect of

cout << x++;
cout << endl << x << end;

(which gives the results you're seeing) or as

cout << x << endl << x << end;
x++;

(which would give 0 both times).

Formally, the results are undefined, so any other
behaviour is possible too. The reason is that, again
sequence points are indicated by semi-colons.

To (hopefully) help you understand the role of sequence points,
the following statement does not include sequence
points between the calls of x() and y().

cout << x() << ' ' << y() << endl;

The order of calling x() and y() is not specified by
the languagage, so the above *could* be expanded
as ...

int t_y = y(); // assume x() and y() return int
int t_x = x();
cout << t_x << ' ' << t_y << endl;

or as ....

int t_x = x(); // note change of order
int t_y = y();
cout << t_x << ' ' << t_y << endl;

It is fairly trivial to detect the order in which x()
and y() are called, but the results will vary between
compilers.

[Snip]

Michiel Salters

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Josh Sebastian wrote:

> OK, I tried this code on MSVC6:

> #include<iostream>
> using namespace std;

> int main() {
> int x = 0;
> x = x++;
> cout << x << endl;
> }

> The output was: 1

> Now, I KNOW for a FACT that MSVC is not 100% conformant to the C++
> standard, so I tend to trust my intuition and logic over the results
> MSVC gives me (for example, MSVC gives me a warning about main not
> returning a value =) But in this case I'm really not sure.
> It seems to me that the x++ occurrs first, as the postfix increment
> operator is higher in precedence than the assignment operator. Thus
> the value at memory location is 1, but the result of the expression
> x++ is 0 (the previous value of x). Then this value (0) should be
> assigned to the memory location identified by x, overwriting the
> effect of the increment. So, I should be getting 0. Is my
> logic/understanding flawed?

Yes. While you're right wrt. the precedence order, that just means you
cant parse the line as (x=x)++;. However, precedence order doesn't say
anything about the order of execution. In general C++ says very little
about the order in which things happen between "sequence points". The
relevant sequence points here are the start of main, and end of the
statement x=x++; (IIRC, int x=0; isn't a statement but a definition)
That means the compiler can decide to do the store of x=0 first, or
do the store of x++ first. Both are correct. Apparently, MSVC++ in this
case decided to store the 0 first, and then overwrite it with 1. It's
also possible that MSVC++ decided to store 1 first, and then discarded
the second assignment since no sequence point was seen between the
assignments.



> I also tried writing:
> int x = 0;
> cout << x++ << endl << x << end;

> and I got
> 0
> 1

Which is a related problem. C++ doesn't specify the order of evaluation
of function arguments. In this case, it seems x++ was evaluated first, and
then x was read after x++ was stored. This is allowed. The simple
example is
void f(int, int);
int g();
int h();
f(g(), h());

You don't know which function will be called first, only that f() will be
called last.



> Then I wrote a little class
> class Number
> {
> int n;
>
> public:
> Number(int num=0) : n(num) { }
> Number& operator++() { ++n; return *this; }
> Number operator++(int) { Number prev = *this; ++n; return
> prev; }
> friend ostream& operator<<(ostream& os, const Number& num);
> };

> ostream& operator<<(ostream& os, const Number& num)
> { return os << num.n; }

> When I replaced Number for int in my first program,

> I got 0

> So that worked like I thought it would. Now, naturally, I DEFINED the
> postfix increment operator to work like I thought it should, but isn't
> this the *correct* way to write it?

This is besides the point. You introduced functions. A function call
introduces one sequence point, after evaluation of all arguments
and before execution of the function body.
As you noticed, there isn't one in the middle of evaluation.



> I can't back this up with a solid argument (mostly because I have a
> very weak understanding of sequence points), but I feel that this is
> related to the (a = b) = c problem that has been discussed recently,
> bucause both statements involve two modifications of a single memory
> location without (I think) a sequence point..

Precisely. In your first example, you're modifying x twice between
sequence points. With the class Number, you introduce a sequence
point between the modifications, and suddenly everything works as
defined.

Michiel Salters

John Potter

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
On 3 Aug 2000 22:11:19 -0400, cur...@earthlink.net (Josh Sebastian)
wrote:

> OK, I tried this code on MSVC6:
>
> #include<iostream>
> using namespace std;
>
> int main()
> {
> int x = 0;
> x = x++;
> cout << x << endl;
> }
>
> The output was:
> 1

It could have formatted your hard drive.

> I can't back this up with a solid argument (mostly because I have a
> very weak understanding of sequence points), but I feel that this is
> related to the (a = b) = c problem that has been discussed recently,
> bucause both statements involve two modifications of a single memory
> location without (I think) a sequence point..

You've got it. Your class did what you expect because both = and ++
are functions which introduce sequence points.

For your builtin example consider

r1 = x
r2 = r1
r2 += 1
sequence point, so do the stores in some order
x = r1 // covers the assignment
x = r2 // now that we are done, lets store the new value in x

Since stores can be done at any time, it could also be

r1 = x
x = r1 // silly but it covers the assignment and y = x ++ works
r1 += 1
x = r1 // now store the new value

My favorite is

x = 3;
cout << (++ x + ++ x) + ++ x << endl;
x = 3;
cout << ++ x + (++ x + ++ x) << endl;

It is not unusual to find that addition is not associative when you
introduce undefined behavior.

John

Stan Brown

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
[This followup was also e-mailed to the cited author for speed.
Please follow up in the newsgroup.]

Josh Sebastian <cur...@earthlink.net> wrote in
comp.lang.c++.moderated:


>OK, I tried this code on MSVC6:

> x = x++;


>
>Now, I KNOW for a FACT that MSVC is not 100% conformant to the C++

>standard, [...]


>It seems to me that the x++ occurrs first, as the postfix increment
>operator is higher in precedence than the assignment operator.

That's not what "precedence" means. Precedence is simply implicit
parentheses to govern how expressions are interpreted, so the
expression
x = x++
means
x = (x++)
and not
(x = x)++.

BUT the precedence does not guarantee that all operations of a
higher precedence will be done before operations of a lower
precedence. All you can count on by way or order of operations is
that everything up to a "sequence point" is done before anything
after that sequence point.

There's a list of sequence points somewhere, but the most
recognizable are
the ; at the end of a statement
the short-circuit operators || and &&

So when you write
x = x++;
you know that x will be incremented, and you know that some value of
x will be assigned to x, but you don't know the order in which those
will happen. Similar ambiguities exist with expressions like
a[x++] = x
and
f(x++, x, x++);
Since << is a function call with ostreams, you have many possible
outputs for
std::cout << x << ' ' << x++ << ' ' << x << ' ' << x++;

Actually, it's even worse than just not knowing what numbers will be
stored or output: the above actually lead to undefined behavior
which means that *anything* could happen.

I don't know where, or if, this problem is explained in the C++ FAQ
(URL in my sig); but it is explained in Steve Summit's excellent C
FAQ, <http://www.eskimo.com/~scs/C-faq/versions.html>. Look
specifically at questions 3.1 through 3.9.

--
Stan Brown, Oak Road Systems, Cleveland, Ohio, USA
http://oakroadsystems.com
C++ FAQ Lite: http://www.parashift.com/c++-faq-lite/
the C++ standard: http://webstore.ansi.org/
illegal identifiers: http://oakroadsystems.com/tech/cppredef.htm
more FAQs: http://oakroadsystems.com/tech/faqget.htm

Bruce DeVisser

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Stan Brown <bra...@mindspring.com> wrote:
> There's a list of sequence points somewhere, but the most
> recognizable are
> the ; at the end of a statement
> the short-circuit operators || and &&

Toward the end of 1.9 of the standard. Since sequence points seemed
rather arcane to me until I got a copy of the standard, let me try to
spell them out for the benefit of those wondering what they are.
Proofreading welcomed, especially on #3. :)


1. The end of each full expression. (1.9/16)
These are usually marked by a ; but can occasionally be marked by
other things, for example, a ) in a class initialiser.
Example:
int main()
{
int x = 5;
std::cout << x << std::endl; // guaranteed to print 5
}

2. At point of function call. (1.9/17)
This relates to evaluation of the function's arguments only.
Example (expanding previous):
int foo(int& x) { return x;}
int main()
{
int x = 5;
std::cout << foo(x=6) << std::endl; // guaranteed to print 6
}

3. At point of return from function call. (1.9/17)
After copying of return value and prior to other expressions
outside of function. I'm a little fuzzy on the meaning of this one.
As I understand it:
Example (expanding previous):
int foo(int& x) { return x -= 2;}
int main()
{
int x = 5;
std::cout << x + foo(x=6) + x << std::endl; // guaranteed to print 12
// But note:
std::cout << foo(x) + foo(x) << std::endl; // undefined behaviour
}

4. (1.9/18) After the first expression ('a' here) in:
a || b
a && b
a , b
a ? b : c

These are harder to show by example, because precedence rules make
you think that the parentheses are doing the work. But here is an
example:
int x =0;
(x = 7) && (x = 5);
std::cout << x << std::endl; // guaranteed to print 5
(x = 7) & (x = 5); // undefined behaviour

Note the ternary operator pitfall: there is only one sequence
point, at the ? (aside from the other pitfall which is that the
second and third expressions are both always evaluated):
int y = 0;
int x = (y = 5) > 0 ? y++ : y; // y guaranteed to be 6 afterwards
int x = (y = 5) > 0 ? y++ : y++; // undefined, two modifications
// to y without sequence point
int x = y > 0 ? y++ : y++; // also undefined

--
- Bruce

Dennis Yelle

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Bruce DeVisser wrote:
> Note the ternary operator pitfall: there is only one sequence
> point, at the ? (aside from the other pitfall which is that the
> second and third expressions are both always evaluated):

I don't think so. Do you have a reference?
When I compile and run this:

#include <iostream>

int main()
{
{
int y = 5;
int x = y < 0 ? y++ : y;
cout << x << ' ' << y << '\n';
}
{
int y = 5;
int x = y > 0 ? y : y++;
cout << x << ' ' << y << '\n';
}
}

with gcc version 2.95.2 19991024 (release)

I get this:
5 5
5 5

which indicates that the second expression is NOT evaluated
when the condition is true, and
that the third expression is NOT evaluated
when the condition is false.

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here: http://table.jps.net/~vert

Carlos Moreno

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Robert O'Dowd wrote:
>
> > I also tried writing:
> > int x = 0;
> > cout << x++ << endl << x << end;
> >
> > and I got
> > 0
> > 1
>
> With this one, the results are also undefined.

No, it's not. The above line has perfectly defined behavior,
since the << involves a function call, which introduces a
sequence point.

> You
> could equally well get 0 both times. The reason is that
> the expression x++ is not guaranteed to be evaluated
> before x.

It is. The above line is parsed as this (or something
equivalent):

(((cout.operator<<
(x++)).operator<<(endl)).operator<<(x)).operator<<(endl);

Before actually calling the first function, the side effect of
x++ must have taken place, since calling a function introduces
a sequence point.

Carlos
--

Erik Max Francis

unread,
Aug 4, 2000, 3:00:00 AM8/4/00
to
Dennis Yelle wrote:

> Bruce DeVisser wrote:
>
> > Note the ternary operator pitfall: there is only one sequence
> > point, at the ? (aside from the other pitfall which is that the
> > second and third expressions are both always evaluated):
>
> I don't think so. Do you have a reference?
> When I compile and run this:

...

Running a test program isn't a very good indication of what the Standard
has to say (the compiler you're using could be nonconforming, after all)

...


> which indicates that the second expression is NOT evaluated
> when the condition is true, and
> that the third expression is NOT evaluated
> when the condition is false.

But you're correct. There is a sequence point after the conditional
expression is evaluated, but before the resultant expression is
evaluated, and exactly one of the second and third expressions are
evaluated. ISO/ANSI C++, 5.16/1.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, US / 37 20 N 121 53 W / ICQ16063900 / &tSftDotIotE
/ \ You are free and that is why you are lost.
\__/ Franz Kafka
Crank Dot Net / http://www.crank.net/
Cranks, crackpots, kooks, & loons on the Net.

Josh Sebastian

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
Thanks for the replies, everyone. I think I understand the problem
better now. I just have two more unresolved (small) issues.

First, I firmly believe that the built-in types should behave in
fundamentally the same manner as a properly defined arithmetic class.
I always wondered why C++ didn't define "functions" to represent
operations on primitive data types (like Ada does). This just
entrenches my belief. So why didn't they?

It seems to me that there wouldn't be a problem if a sequence point
were introduced between the left and right sides of the assignment
operator. In fact, it seems to me that there should be a sequnce point
between ALL binary operators. This could, in lieu of making all
primitive operators functions, make a primitive data type and an
arithmetic class behave identically.


______________

"Yields falsehood when preceded by its quotation" yields falsehood when
preceded by its quotation!

Josh Sebastian
<cur...@earthlink.net>

Dennis Yelle

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
Josh Sebastian wrote:
[...]
> First, I firmly believe that the built-in types should behave in
> fundamentally the same manner as a properly defined arithmetic class.
> I always wondered why C++ didn't define "functions" to represent
> operations on primitive data types (like Ada does). This just
> entrenches my belief. So why didn't they?
>
> It seems to me that there wouldn't be a problem if a sequence point
> were introduced between the left and right sides of the assignment
> operator. In fact, it seems to me that there should be a sequnce point
> between ALL binary operators. This could, in lieu of making all
> primitive operators functions, make a primitive data type and an
> arithmetic class behave identically.

I don't know if your comments above will be taken seriously or not.
It is one of those deep and interesting questions that can be
asked by a novice, so some people just do not know how to respond.

One reason is that most of the built-in types were inherited from C and,
in the beginning, it was considered important that they behave
exactly the same in C and C++. Some people think that we cannot
do what you suggest without either breaking compatibility with C
or making programs run much slower than they do today or both.
I, myself, am not convinced. It is very difficult to prove something
is impossible. On the other hand, I am too lazy to try to prove
that it is not impossible.

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here: http://table.jps.net/~vert

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bruce G. Stewart

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
Bruce DeVisser wrote:

> Note the ternary operator pitfall: there is only one sequence
> point, at the ? (aside from the other pitfall which is that the
> second and third expressions are both always evaluated):

> int y = 0;
> int x = (y = 5) > 0 ? y++ : y; // y guaranteed to be 6 afterwards
> int x = (y = 5) > 0 ? y++ : y++; // undefined, two modifications
> // to y without sequence point

> int x = y > 0 ? y++ : y++; // also undefined
>


No no no. Only one of the alternatives is evaluated.

"There is only one sequence point, at the ?" forbids

x = 1 + (y == 0) ? (x = 5) : z;

but allows:

x = (x++ == 0) ? y : z;

The store x in x++ must be completed before the sequence point
associated with ?.

I would think that x = b ? y++ : y--; is well defined: x and y will each
be updated once for any execution of the statement.

Bruce DeVisser

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
Dennis Yelle <denn...@jps.net> wrote:
> Bruce DeVisser wrote:
>> Note the ternary operator pitfall: there is only one sequence
>> point, at the ? (aside from the other pitfall which is that the
>> second and third expressions are both always evaluated):

> I don't think so. Do you have a reference?

Yes, I have a reference, and you are correct. Sheesh, a basic
concepts, you'd think I haven't read anything. Well, at least you know
one thing about my coding style: I don't put side-effect expressions
into the second and third expressions of a conditional; if I did, I'd
know better.

5.16/1 says:

Conditional expressions group right-to-left. The first expression
is implicitly converted to bool (clause 4). It is evaluated and if
it is true, the result of the conditional expression is the value of
the second expres- sion, otherwise that of the third expression. All
side effects of the first expression except for destruction of
temporaries (12.2) happen before the second or third expression is
evaluated. Only one of the second and third expressions is
evaluated.

Thanks for the correction, and my apologies for the rotten example.

--
- Bruce

Francis Glassborow

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
In article <MPG.13f404e65...@news.mindspring.com>, Stan Brown
<bra...@mindspring.com> writes

>BUT the precedence does not guarantee that all operations of a
>higher precedence will be done before operations of a lower
>precedence.

Well actually it sort of does, but it says nothing about the order in
which sub-expressions and operands are evaluated except that they must
be evaluated by the time the operator is evaluated.


Francis Glassborow Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Josh Sebastian

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
Well, thank you for responding!

I can see how it may compromise combatablity with C but not how it
would sacrifice speed. After all, there are already several operators
that have function-like syntax. Namely, the new-style casts and the
sizeof operator. And sizeof was even part of C!

It would raise many difficulties though. Oh well. I can live with it.


______________

"Yields falsehood when preceded by its quotation" yields falsehood when
preceded by its quotation!

Josh Sebastian
<cur...@earthlink.net>

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Francis Glassborow

unread,
Aug 5, 2000, 3:00:00 AM8/5/00
to
In article <398B1254...@jps.net>, Dennis Yelle <denn...@jps.net>
writes

>Bruce DeVisser wrote:
>> Note the ternary operator pitfall: there is only one sequence
>> point, at the ? (aside from the other pitfall which is that the
>> second and third expressions are both always evaluated):

Pitfall? No, it is your misunderstanding. The compiler MUST NOT evaluate
the both the second and third expressions but only the appropriate one.


Francis Glassborow Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

ivec

unread,
Aug 6, 2000, 3:00:00 AM8/6/00
to
Dennis Yelle said:
>
>Josh Sebastian wrote:
>[...]
>> First, I firmly believe that the built-in types should behave in
>> fundamentally the same manner as a properly defined arithmetic class.
>> I always wondered why C++ didn't define "functions" to represent
>> operations on primitive data types (like Ada does). This just
>> entrenches my belief. So why didn't they?
>>
>> It seems to me that there wouldn't be a problem if a sequence point
>> were introduced between the left and right sides of the assignment
>> operator. In fact, it seems to me that there should be a sequnce
point
>> between ALL binary operators. This could, in lieu of making all
>> primitive operators functions, make a primitive data type and an
>> arithmetic class behave identically.
...

>One reason is that most of the built-in types were inherited from C
and,
>in the beginning, it was considered important that they behave
>exactly the same in C and C++.

But adding sequence points basically only changes into specified
behaviour what was previously unspecified. Specifying something
in C++ where C was allowed to do anything would not be a concern
for backwards compatibility of user code.

>Some people think that we cannot
>do what you suggest without either breaking compatibility with C
>or making programs run much slower than they do today or both.

Sequence points are needed for the result of a program to be defined.
However, adding too many sequence points does impair the compiler's
ability to produce optimized code.
For example, reordering assembly instructions can have a major
performance impact on modern processors, as well as the caching
of variables that have to be read from memory.
But this does not really matter.

Why would you want to have more sequence points anyway ?
What would it bring other than new opportunities for
creating obfuscated code ?
I would never write code like x = ++x or y = ++x + (z=++x)
even if their result was well defined.
Take a well specified thing like operator precedence: most
coding style guidelines will require the use of parentheses
rather than to rely on implicit order. Because if you read code that
says a = b == c << d & e , even you know exactly what it does, you
can't usually be sure that the guy who wrote it knew it well too.
The behavior is specified, but what is the benefit ?

When you face undefined behaviour with the current sequence points,
you probably should be breaking up your code statements anyway.

Look at the flexibility we have with type expressions (e.g.
typedef struct tag (*)(char*)(*f[5])(double (*)[3]); ),
Is there any real benefit in being allowed to write such things ?

>I, myself, am not convinced. It is very difficult to prove something
>is impossible. On the other hand, I am too lazy to try to prove
>that it is not impossible.

My own perspective would be that I do not care about what
is possible, but what is practical.

True, there are discrepancies between the behaviour of built-in
and user-defined operators, but they are easy to keep ouside of
the realm of useful C++ constructs - in my humble experience.

Any counter-examples ?


Regards,
Ivan

--
"Si on avait pu naitre avant l'Homme !" - Cioran
----------------------------------------------------
Ivan Vecerina, Dr. med. , Senior Software Engineer
http://www.surgnav.com - http://www.cedara.com


Sent via Deja.com http://www.deja.com/
Before you buy.

Michiel Salters

unread,
Aug 7, 2000, 3:00:00 AM8/7/00
to
Carlos Moreno wrote:

> Robert O'Dowd wrote:

[ OP' name got lost somewhere ]

> > > I also tried writing:
> > > int x = 0;

> > > cout << x++ << x << end;


> > > and I got
> > > 0 1

> > With this one, the results are also undefined.

> No, it's not. The above line has perfectly defined behavior,
> since the << involves a function call, which introduces a
> sequence point.

[ SNIP ]

> The above line is parsed as this (or something
> equivalent):

> (((cout.operator<< (x++)).operator<<(x)).operator<<(endl);



> Before actually calling the first function, the side effect of
> x++ must have taken place, since calling a function introduces
> a sequence point.

True. But at the moment the first function is called x might already
have been evaluated. The reason is that for member functions, there
is no sequence point between evaluating the lhs and the arguments,
i.e. if we have GetObjRef().Member(GetArg()) there is no sequence
point between GetObjRef() and GetArg(), and they may happen in any
order.

By this rule, the first part of your expression (which seems correct
to me), (cout.operator<< (x++) may be evaluated before or after
evaluating x in .operator<<(x). Thus x may be 0 or 1.

Ergo, this leads to unspecified behavior. I don't think the compiler
is allowed to do anything than choose any order it likes, possibly
even a random order. But that's the only degree of freedom it has.
It's neither defined nor undefined.

Regards,
Michiel Salters

John Potter

unread,
Aug 13, 2000, 3:00:00 AM8/13/00
to
On 4 Aug 2000 12:35:32 -0400, Bruce DeVisser <br...@bmdsoftware.com>
wrote:

> Toward the end of 1.9 of the standard. Since sequence points seemed
> rather arcane to me until I got a copy of the standard, let me try to
> spell them out for the benefit of those wondering what they are.
> Proofreading welcomed, especially on #3. :)

> 3. At point of return from function call. (1.9/17)


> After copying of return value and prior to other expressions
> outside of function. I'm a little fuzzy on the meaning of this one.

Me too. An example would sure help. I don't have one either.

> As I understand it:
> Example (expanding previous):
> int foo(int& x) { return x -= 2;}

There is a sequence point at the end of the expression x -= 2. The
value of this expression is copied to a temporary int and then there
is another sequence point? What if there were local objects with
dtors? Could they be run in parallel with calling code or are they
outside of the function? Would it make a difference? I think they
are functions and can not be run in parallel; however, do they need
to be run prior to calling code?

> int main()
> {
> int x = 5;
> std::cout << x + foo(x=6) + x << std::endl; // guaranteed to

> // print 12

Nope. The only sequence point of interest is the function call. The
only requirement is that its parameter be evaluated prior to the call.
The two other evaluations of x can be performed in any order before or
after the call.

x (5), x (5), x = 6, foo (4) 14
x (5), x = 6, x (6), foo (4) 15
x (5), x = 6, foo (4), x (4) 13
x = 6, x (6), x (6), foo (4) 16
x = 6, x (6), foo (4), x (4) 14
x = 6, foo (4), x (4), x (4) 12

Note that this is unspecified behavior because of the order of
evaluation of the operands of +. Since some of those allowed sequences
use the old value of x and modify x without a sequence point, we get
undefined behavior. The sequence points do not prohibit the
implementation from generating code which uses the old value of x, they
prohibit the user from writting code which allows it and expecting
anything reasonable.

> // But note:
> std::cout << foo(x) + foo(x) << std::endl; // undefined behaviour
> }

Here we have sequence points and pass by reference. It makes no
difference when we evaluate the operand of foo because it is always
the lvalue x. The lvalue to rvalue conversion takes place within
foo.

foo1 (3), foo2 (1) 4
foo2 (3), foo1 (1) 4

If the operator were - rather than +, the unspecified result would
be either 2 or -2, but there is no undefined behavior.

Replacing foo(x=6) with foo(x) in the first example gives

x (5), x (5), foo (3) 13
x (5), foo (3), x (3) 11
foo (3), x (3), x (3) 9

with no undefined behavior.

Is that sequence point at function return after copying the result to
the return area or after copying it from the return area? In this
example, the return area is likely a register and there is no copy
from it.

Can anyone give an example where a program would have well defined
behavior with the return sequence point and unspecified or undefined
behavior without it?

John

Bruce DeVisser

unread,
Aug 15, 2000, 3:00:00 AM8/15/00
to
John Potter <jpo...@falcon.lhup.edu> wrote:
> On 4 Aug 2000 12:35:32 -0400, Bruce DeVisser <br...@bmdsoftware.com>
> wrote:

> There is a sequence point at the end of the expression x -= 2. The
> value of this expression is copied to a temporary int and then there
> is another sequence point? What if there were local objects with
> dtors? Could they be run in parallel with calling code or are they
> outside of the function? Would it make a difference? I think they
> are functions and can not be run in parallel; however, do they need
> to be run prior to calling code?

Hmm. Are you referring to something like this?:

using namespace std;

struct Foo
{
Foo(int& x) : mX(x) {}
~Foo() { mX = 3; }
int& mX;
};

int Bar(int& x)
{
Foo foo(x);
return x = 4;
}

int main()
{
int x = 5;

cout << Bar(x) << endl;
cout << x << endl;
cout << x = Bar(x) << endl; // hmmm
}

There is a sequence point at the end of the return statement; there is
a sequence point prior to the evaluation of expressions outside
Bar(x); therefore this is well formed (I hope... not that I would ever
want to use it), and the output should be
4
3
4


>> int main()
>> {
>> int x = 5;
>> std::cout << x + foo(x=6) + x << std::endl; // guaranteed to
>> // print 12

> Nope. The only sequence point of interest is the function call.
> The only requirement is that its parameter be evaluated prior to the
> call. The two other evaluations of x can be performed in any order
> before or after the call.

Okay, I can now see that you are right. I was misunderstanding the
standard at this point.


> Is that sequence point at function return after copying the result to
> the return area or after copying it from the return area? In this
> example, the return area is likely a register and there is no copy
> from it.

I assume it is as if the return statement did the following:

1. Evaluate the return expression, as indicated by the sequence point
at the end of the return statement. Since temporaries (within the
return statement) are destroyed at the end of a statement, and
presumably this includes the return value (big presumption), copy the
value to the 'holding area' before the sequence point.

2. Destroy all auto objects used by the function, using the
end-of-function sequence point.

3. Resume evaluation of calling expression, using the 'holding area'
as a temporary that replaces the function call.

But I may be barking up the wrong tree entirely. Footnote 11, from
1.9/17, seems to say that really this is not an 'extra' sequence
point, but is mentioned to deal with anomalous situations such as
function exit via throw... which is perhaps your point.


> Can anyone give an example where a program would have well defined
> behavior with the return sequence point and unspecified or undefined
> behavior without it?

Check the last line of main() up above. But for anything useful, there
are functions involved, and the sequence points clear up
automatically.

It seems the closer I look at the standard, the less clear it all
becomes.:P

--
- Bruce

Reply all
Reply to author
Forward
0 new messages