what is the right value about a = a++;

M

Markus Wichmann

The question is posted in this group many times.
Read the FAQ. The result is undefined - that is,
it is DEFINED to be undefined. That means it is
okay for the result to be different in different
compilers, and it is even okay for it to be
different in the same compiler in different
places or at different times.

On a related note, what about the following:

static int foo;
static int bar(void)
{
return foo++;
}

int main(void)
{
return foo++ - bar();
}

The standard sticks a sequence point between the evaluation of a
function call's arguments but not after its return. bar() is not
guarenteed to be called before or after evaluation of foo++, so bar()
can have different results. So does the above program exhibit UB?

Because if it does, that means that no-one should use function calls in
compound expressions. Unless the function called is pure or there's only
one function call which is compounded only with local variables (i.e.
variables with a scope smaller than file scope).

Ciao,
Markus
 
K

Kaz Kylheku

On a related note, what about the following:

static int foo;
static int bar(void)
{
return foo++;
}

int main(void)
{
return foo++ - bar();
}

I would have to say that this is unspecified behavior, not undefined.
There are these possibilities.

Scenario 1:

The processing of the foo++ in main takes place like this. First foo
has to be accessed, and then the new value stored. So the call to bar can occur
in three abstract places: 1) before foo is accessed, 2) after it is accessed but
before the new value is stored, or 3) after the new value is stored.

Case 1 is similar to this:

int temp = bar();
return foo++ - temp; /* foo becomes 2, 1 returned */

Case 2 is similar to this:

int temp0 = foo, temp1 = bar(); /* 0, 0 */
return foo = temp0 + 1, temp0; /* foo becomes 1, 0 returned */

Case 3:

int temp = foo++; /* 0, foo becomes 1 */
return temp - bar(); /* foo becomes 2, -1 returned */

Notice the symmetry between case 1 and 3. 2 is the most complex; we need
to expose its semantics using two temporary values. This is because the call to
bar() is interposed into the evaluation of foo++.

Scenario 2:

foo++ works by accessing the value, adding 1 to it, storing the new value,
accessing the value again and then subtracting 1 to produce the resulting
value. If foo is not volatile, this is allowed because the accesses are not
visible behavior. Now bar() can be called first, after the value is accessed,
after it is stored, then after it is accessed again. Here you have four cases
that can be worked out similarly to Scenario 1.
 
K

Kaz Kylheku

I would have to say that this is unspecified behavior, not undefined.
There are these possibilities.

By the way I did consider the possibility that the access to foo may not be
atomic. I don't think that a function call can be issued in the middle of an
access to a global, because it seems that the sequence point isn't honored in
that case, but I will chew on that one.

The main thing about this sequence point crap is that you can look smart while
discussing it, even if the conclusions are vague.
 
S

Stephen Sprunk

Or you could wake up and realize that the stack can be indexed like
an array, unless you're working with a target language that really
has only push and pop, with no other access to the stack. (Good luck
implementing local variables efficiently, etc).

You can allocate the space for N arguments, and then store valuews
into that stack space in whatever order is convenient, while obeying
the abstract evaluation order (in those situations where it
potentially makes a difference).

Once again, conformity versus efficiency.

The efficient:

PUSH C
PUSH B
PUSH A

versus the conforming:

SUB SP,12
STOR [SP],A
STOR [SP+4],B
STOR [SP+8],C

(Note, of course, that it is possible that there are machines on which
the calculated-store is faster than push, and may improve speed even
with the added stack-adjust, in which case it is certainly free to
generate such code. The problem comes when you mandate it.)

Note that many modern CPUs can execute the latter version's three stores
in parallel, whereas the three simple pushes have an implicit dependency
on the stack pointer and may have to be executed serially, so the latter
is what compilers usually emit today.

However, that _wasn't_ the case back when all this stuff was being
decided: everything was serial, so the former version was faster as well
as smaller, both of which were a big deal back in the day.

S
 
K

Kaz Kylheku

There is no guarantee that this will give the same result "no matter what":

foobar(printf("foo"),printf("bar"));

No guarantee /now/. In this discussion thread, you are required to think
outside of the box, and keep a clean mental separation between the language as
it is, and the fantasy language in which evaluation orders are constrained
that is being discussed.
But the Standard makes no guarantees that your sample is the "as-if" that
must result.

The "compiler" above refers to one which ensures left to right,
bottom-up evaluation order. I.e. the behavior of
{ int temp0 = i++, temp1 = i++; f(temp0, temp1); } could be obtained
simply by expressing f(i++, i++) with such a compiler.
 
K

Kaz Kylheku

On 2/8/2012 7:18 PM, Kaz Kylheku wrote:
[...]
Or you could wake up and realize that the stack can be indexed like an array,
unless you're working with a target language that really has only push and
pop, with no other access to the stack. (Good luck implementing local
variables efficiently, etc).

You can allocate the space for N arguments, and then store valuews into
that stack space in whatever order is convenient, while obeying
the abstract evaluation order (in those situations where it potentially
makes a difference).

Once again, conformity versus efficiency.

The efficient:

PUSH C
PUSH B
PUSH A

This is conforming, too if C, B and A don't have any side effects.
 
K

Kaz Kylheku

foo(a++,a++);

On my system, this code is generated:

mov eax, DWORD PTR _a
push eax
push eax
add eax, 2
mov DWORD PTR _a, eax
call _foo
add esp, 8

By delaying the post-increment, the code is more efficient than requiring
two separate increments.

Note that this is still allowed under strict left-right, bottom-up evaluation.

Because a is not volatile, the code does not require two actual increments.
That is merely the abstract semantics.

So this is really tangential to the debate. (Yes, rearranging computations is
important and crucial necessary for optimization.)
 
J

Joshua Maurice

Fact is, defined order is safe. Safety is the number one goal in engineering.

The entire argument of this thread can probably safely be skipped,
given that this assertion has been made.

In short, Kaz is wrong. There are always tradeoffs between safety,
cost, time to market, maintainability, extensibility, and so on.
Sometimes they do go hand in hand. Sometimes they clearly don't.
Different projects have different concerns, different weights on these
tradeoffs.

Nothing further need be said to safely dismiss this incredibly naive
view espoused by Kaz.
 
J

James Kuyper

Then if they did whatever they thought was best then that was what
they pleased. I'm not sure what you think "whatever they pleased"
means.

"What they thought was best" is not, in general, the same as "What
pleased them most". For instance, for some implementors, it might have
pleased them most to insert warning messages that made fun of people
whose politics they disagreed with. But what they thought was best
probably didn't include losing that portion of their potential market
which would have been offended by such messages, nor that additional
share of their potential market that might have felt that such warning
messages showed insufficient professionalism.

I tried to come up with an example that more directly applies the
evaluation order of operands, but I had a hard time coming up with any
plausible examples where "pleasure" could be a significant issue.
Efficiency, ease of implementation, compatibility with other
implementations - I can see them all as being relevant, but I couldn't
figure out how to bring "pleasure" in to the mix for evaluation order
issues.
 
J

James Kuyper

On Wed, 08 Feb 2012 15:39:33 -0500, Kenneth Brody
In my 30 odd years of programming in C I've come across cases where

That sentence would work better with a quantifier of some kind before
the word "cases" - "many", "few", "a fair number", etc.
the code only worked because of side-effects being completed in the
right order.

You weren't the author, I hope? Can you provide an example?
 
J

James Kuyper

....
Well, no, C was intended as a convenient portable replacement for
assembly language in systems programming. Safe and fast are not
opposites.

Nor are they they same; as with any two characteristics that aren't
highly correlated, you can and often do have trade-offs between them.
 
S

Shao Miller

These evaluation order issues fall into a category that is not caught
by tools, and not even by perfect test coverage, because a test case
could simply validate that the expected behavior is happening on the
given target machine and compiler.

What is an example use case for having a defined evaluation order for
function arguments? I might have missed one or more.

Is it realistic that an implementation might have a thread of execution
for each independent branch of evaluation, so that the evaluation order
of some expressions might be simultaneous?
 
8

88888 Dihedral

在 2012å¹´2月9日星期四UTC+8上åˆ9æ—¶10分29秒,Kaz Kylheku写é“:
Since a is not volatile and those transcendental functions have no side
effects, this evaluation order would be allowed even if the evaluation order
for A * B is that A is evaluated first.

Why don't we change this to:

b = sqrt(sin(x)*cos(x)) && a;

Or how about:

b = sqrt(sin(x)*cos(x)), a;

Both && and , have ordering properties today.

If a is zero, do you believe that the trigonometry has to be evaluated?

sqrt(sin(x)*cos(x))=sqrt(sin(x*2)*0.5)

Even the sqrt function call can be simplified by a numerical
analysis, but this is not the language or compiler's job now.

Thus, it is time to distinguish different programming tools
for different jobs in different applications.

I don't believe that one computer language can solve all kinds of
tasks as a religeion proposed by some authors.
 
S

Shao Miller

I would have to say that this is unspecified behavior, not undefined.
There are these possibilities.

In C11, the evaluation of 'foo++' is indeterminately sequenced relative
to the call to 'bar'. It is either before or after, but not a portion
before and a portion after.[5.1.2.3p3, 6.5.2.2p10]

Then the behaviour doesn't appear to fall into 6.5p2, since it is no
longer unsequenced given the consideration above. So yes, it looks to
be unspecified, in C11.
 
P

Philip Lantz

Kaz said:
I would have to say that this is unspecified behavior, not undefined.
There are these possibilities.

Scenario 1:

The processing of the foo++ in main takes place like this. First foo
has to be accessed, and then the new value stored. So the call to bar can occur
in three abstract places: 1) before foo is accessed, 2) after it is accessed but
before the new value is stored, or 3) after the new value is stored.

Case 1 is similar to this:

int temp = bar();
return foo++ - temp; /* foo becomes 2, 1 returned */

Case 2 is similar to this:

int temp0 = foo, temp1 = bar(); /* 0, 0 */
return foo = temp0 + 1, temp0; /* foo becomes 1, 0 returned */

Case 3:

int temp = foo++; /* 0, foo becomes 1 */
return temp - bar(); /* foo becomes 2, -1 returned */

Notice the symmetry between case 1 and 3. 2 is the most complex; we need
to expose its semantics using two temporary values. This is because the call to
bar() is interposed into the evaluation of foo++.

I don't think your case 2 is allowed. If foo++ is evaluated before the
call to bar(), then the sequence point before the call ensures that the
side effect is complete before the call.
 
K

Kaz Kylheku

Apparently. The text I sent was:

Granted there is no point in whining about C's lack of a well defined
evaluation.

Nobody is "whining". I made a well-considered criticism.

C would be a better language without gratuitous behaviors in the way abstract
expressions are put together.
C is what it is and "undefined" is pretty much carved in
stone.

C is obviously not carved in stone, which is why there are so many C's to
choose from. K&RC, C90, C99, C11 ...

Once upon a time it might have been "whining" to want better type checking
between function declarations and definitions.
But it has very little to do with optimization and a lot to do
with "that's the way things are; suck it up cupcake and live with it."

That is neither here nor there.

However, I might just implement this strict evaluation in GCC one of these days
as an option. That would be a good start to actually get it done.

However, there is value in discussing the idea, too.
 
J

James Kuyper

On Thu, 09 Feb 2012 23:10:46 -0500, James Kuyper


Stop right there. "whatever they pleased" and "What pleased them most"
are two different things.

True - and yet, if you took every occurrence of "what pleased them
most", or variations of the same, from my message, and replace it with a
corresponding variation on "whatever they pleased", the message would
remain equally true.
 
J

James Kuyper

Few works. The problem is that in times of yore (pre-ansi) the rules
were different. The ultimate authority about the legitimacy of C code
on a given machine was the compiler. If your code had to run on
twenty different machines then you built and tested it on all of those
machines. Any thing that didn't work, regardless of whether or not it
"should have" was a bug.

Well, yes - pre-ANSI was a different issue. In the post-ANSI C world,
however, any code that relied upon the order in which side-effects
occurred, when that order was unspecified by the C standard, was
defective. In general, I'm a big believer in the concept that not all
code needs to be portable - but this particular kind of non-portability
doesn't seem to have any value. You can always re-write the code to
guarantee the desired order of side-effects, and the result of doing so
is usually code that would still be clearer than the original even if
the standard were changed to specify the order of the side effects in
the original.
 
B

Ben Bacarisse

James Kuyper said:
True - and yet, if you took every occurrence of "what pleased them
most", or variations of the same, from my message, and replace it with a
corresponding variation on "whatever they pleased", the message would
remain equally true.

I can't make that work. Your usage emphasises (explicitly) the idea of
pleasure being involved, and that is surely the difference Richard
Harter is pointing out. "What pleased them most" is about pleasure and
gratification, but "whatever they please" is about having "the will or
desire; to have the inclination or disposition; to think proper; to
choose" (though having pleasure in the result is not excluded, of
course).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,082
Messages
2,570,589
Members
47,212
Latest member
JaydenBail

Latest Threads

Top