why is it so ?

J

junky_fellow

Why

i = ++i; is undefined
but
i = func(++i); is defined ?

I know there is a sequence point after a call to a function
after the arguments have been evaluated. But I am not able
to visualise how the sequence point makes the second statement
defined ? Also can someone give me a detailed explanation of
why first statement is undefined ?

Thanx for any help in advance...
 
R

Robert Gamble

Why

i = ++i; is undefined
but
i = func(++i); is defined ?

I know there is a sequence point after a call to a function
after the arguments have been evaluated. But I am not able
to visualise how the sequence point makes the second statement
defined ?

See the recent thread entitled "Sequence points and function calls" on
comp.std.c.
Also can someone give me a detailed explanation of
why first statement is undefined?

The standard states that an object shall not have its stored value
modified more than once between sequence points, your example modifies
i twice without an intervening SP, its pretty straight-forward I think.
If you still don't understand you'll have to be more specific about
what are having trouble grasping.

Robert Gamble
 
J

junky_fellow

Robert Gamble wrote:
The standard states that an object shall not have its stored value
modified more than once between sequence points, your example modifies
i twice without an intervening SP, its pretty straight-forward I think.
If you still don't understand you'll have to be more specific about
what are having trouble grasping.

Robert Gamble

Still I don't understand what's the harm in doing that. Can
you please show (step by step) how this will lead to different results
on different complilers ?
 
R

Robert Gamble

Robert Gamble wrote:


Still I don't understand what's the harm in doing that.

The harm? It invokes undefined behavior.
Can you please show (step by step) how this will lead to different results
on different complilers ?

Outside of the aforementioned restriction placed by the Standard, there
is only one logical way to evaluate the expression i=++i, the same way
that i=++j would be evaluated, with the value of i being increased by
one in the former example. But the Standard does place the restriction
there and since i is modified twice (once for the increment operator
and once for the assignment operator) the behavior is undefined. I
don't know exact the reasoning behind the restriction with regards to
non-ambiguous expressions but suspect it has to do with the complexity
involved with trying to come up with wording to define what is
ambiguous and what is not.

Robert Gamble
 
C

Chris Torek

Outside of the aforementioned restriction placed by the Standard, there
is only one logical way to evaluate the expression i=++i ...

Which "one way" is that? Is that the one that goes:

"increment variable i as stored in register %l2, then
register %l2 to register %l2"

or would perhaps be:

"remember to increment register %l2; then compute %l2+1
with result going into %l2; then execute remembered increment,
so increment register %l2"

? Note that the latter winds up with "i" increasing by two
instead of 1, but both emit two instructions:

inc %l2
mov %l2,%l2

or:

add %l2,1,%l2
inc %l2

and "add" and "mov" are both single-cycle instructions ("mov" is
an assembler alias for "logical-or with register %g0").
 
K

Keith Thompson

Robert Gamble wrote:


Still I don't understand what's the harm in doing that. Can
you please show (step by step) how this will lead to different results
on different complilers ?

The statement in question was

i = i++;

It exhibits undefined behavior because the standard says it invokes
undefined behavior.

Optimizing compilers can perform various transformations on your code;
the generated assembly or machine code may bear little obvious
resemblance to what you wrote, except that it behaves the same way.
Since undefined behavior can do anything, the optimizer is allowed to
assume that there is no undefined behavior. If this assumption turns
out to be incorrect, the results can be arbitrarily bad -- and it's
your fault, not the optimizer's. "If you lie to the compiler, it will
get its revenge." -- Henry Spencer

And in this particular case, nothing would be gained by defining the
behavior. There is no reason to write
i = i++;
in the first place. If you want to increment i, just write
i++;
Assigning the result back to i would be superfluous even if it worked.
 
R

Robert Gamble

Chris said:
Which "one way" is that? Is that the one that goes:

"increment variable i as stored in register %l2, then
register %l2 to register %l2"

This makes sense (assuming a non-optimizing compiler)
or would perhaps be:

"remember to increment register %l2; then compute %l2+1
with result going into %l2; then execute remembered increment,
so increment register %l2"

This might make sense if the expression was i = i++ but I don't think
this would be a valid way to handle i = ++i.

Robert Gamble
 
F

Flash Gordon

Robert said:
This makes sense (assuming a non-optimizing compiler)




This might make sense if the expression was i = i++ but I don't think
this would be a valid way to handle i = ++i.

Well, here is a way it could do it on a processor that can run
instructions in parallel:
t = i + 1
In parallel write t to i and increment i
SIGINSTRUCTIONCLASH
i.e. bang because two instructions are writing to the same location at
the same time.

I don't know if there are any such implementations, but there are
definitely processors with multiple pipelines, so it could happen.
 
M

Mark McIntyre

On 19 Jul 2005 04:10:33 -0700, in comp.lang.c ,
Why

i = ++i; is undefined
but
i = func(++i); is defined ?

I know there is a sequence point after a call to a function
after the arguments have been evaluated. But I am not able
to visualise how the sequence point makes the second statement
defined ?

At a sequence point, the effect of all preceding operations must be
complete. Therefore the effect of ++ must be complete. Therefore
passing it to a function is defined.
Also can someone give me a detailed explanation of
why first statement is undefined ?

Axiomatically, the order in which things between sequence points are
done, is undefined.
 
M

Mark McIntyre

On 19 Jul 2005 07:22:55 -0700, in comp.lang.c ,
(e-mail address removed) wrote:

(of invoking UB via modifying an object twice between sequence points.
Still I don't understand what's the harm in doing that. Can
you please show (step by step) how this will lead to different results
on different complilers ?

First you tell us what the result is. Hint: there are multiple
possibilities, even excluding the use of parallel processor pipelines
to execute the store and increment.
 
B

Barry Schwarz

Why

i = ++i; is undefined
but
i = func(++i); is defined ?

I know there is a sequence point after a call to a function
after the arguments have been evaluated. But I am not able
to visualise how the sequence point makes the second statement
defined ? Also can someone give me a detailed explanation of
why first statement is undefined ?
In the first example, the ++ operator returns the incremented value of
i but does not necessarily increment storage until a sequence point is
reached. The only sequence point is the terminating semicolon. So it
would be possible for the ++ operator to increment the value in i
before or after the value is changed by the = operator. This allows
for two different possible results in i which is obviously
intolerable. So the standard requires this to be undefined behavior.

In the second example, there is a sequence point after the argument is
evaluated but before the function is called. Therefore, the update to
i due to the ++ operator is performed before the function is called.
After the function returns, the value of i is updated again by the =
operator but there is no other update "pending" and therefore no
potential ambiguity.



<<Remove the del for email>>
 
E

Eric Sosman

Barry said:
In the first example, the ++ operator returns the incremented value of
i but does not necessarily increment storage until a sequence point is
reached. The only sequence point is the terminating semicolon. So it
would be possible for the ++ operator to increment the value in i
before or after the value is changed by the = operator. This allows
for two different possible results in i which is obviously
intolerable. So the standard requires this to be undefined behavior.

In the second example, there is a sequence point after the argument is
evaluated but before the function is called. Therefore, the update to
i due to the ++ operator is performed before the function is called.
After the function returns, the value of i is updated again by the =
operator but there is no other update "pending" and therefore no
potential ambiguity.

Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
between evaluating `++i' and starting to execute `f', and
there's even another one before `f' returns its value. But
I don't think this guarantees a sequence point between the
evaluation of `++i' and the assignment to the l.h.s.

Now, "it stands to reason" that the assignment cannot
occur until after `f' returns its value, and `f' cannot
return its value until it's been called, so the sequence
point at the call should suffice. But I don't think this
argument is reliable: What if the compiler can predict the
value `f' will return without actually calling it at all?
For example,

int f(int x) {
printf ("%d bottles of beer on the wall\n", x);
return 0; /* 0 = success, -1 = failure */
}

is "predictable," and I think the compiler would be within
its rights to zero `i', call `f', and ignore the returned
value. Lord only knows what would happen to the `++',
or what might appear on stdout. Old Frothingslosh, if
your luck is bad.
 
R

Robert Gamble

Eric said:
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
between evaluating `++i' and starting to execute `f', and
there's even another one before `f' returns its value. But
I don't think this guarantees a sequence point between the
evaluation of `++i' and the assignment to the l.h.s.

Now, "it stands to reason" that the assignment cannot
occur until after `f' returns its value, and `f' cannot
return its value until it's been called, so the sequence
point at the call should suffice. But I don't think this
argument is reliable: What if the compiler can predict the
value `f' will return without actually calling it at all?
For example,

int f(int x) {
printf ("%d bottles of beer on the wall\n", x);
return 0; /* 0 = success, -1 = failure */
}

is "predictable," and I think the compiler would be within
its rights to zero `i', call `f', and ignore the returned
value.

I don't think so. A very similiar example was provided recently in the
thread entitled "Sequence points and function calls" in comp.std.c. In
response, Peter Nilsson noted that such optimization "cannot induce
undefined behavior
in a case where the abstract semantics are well defined" and points out
an example in the Standard to support this statement (Example 6 in
5.1.2.3). Antoine Leca also noted "it is possible to allow the
compiler to
optimise by storing 0 in i early (or for example, on a different CPU),
but
not earlier than the fetch of the previous value of i to feed into the
call." and provided relevant citations from the Standard. I agree with
their reasoning and can't find fault with their arguments.

Robert Gamble
 
K

Keith Thompson

Eric Sosman said:
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
between evaluating `++i' and starting to execute `f', and
there's even another one before `f' returns its value. But
I don't think this guarantees a sequence point between the
evaluation of `++i' and the assignment to the l.h.s.

Now, "it stands to reason" that the assignment cannot
occur until after `f' returns its value, and `f' cannot
return its value until it's been called, so the sequence
point at the call should suffice. But I don't think this
argument is reliable: What if the compiler can predict the
value `f' will return without actually calling it at all?
For example,

int f(int x) {
printf ("%d bottles of beer on the wall\n", x);
return 0; /* 0 = success, -1 = failure */
}

is "predictable," and I think the compiler would be within
its rights to zero `i', call `f', and ignore the returned
value. Lord only knows what would happen to the `++',
or what might appear on stdout. Old Frothingslosh, if
your luck is bad.

The whole purpose of sequence points, I think, is to impose a
reasonable set of of restrictions on what optimizations a compiler is
allowed to perform. An optimizer *can* move a side effect across a
sequence point, but it's allowed to do so only if it doesn't destroy
the semantics of the program. The actual program needs to behave,
within certain limits, in a manner consistent with the way the program
behaves in the C abstract machine.

For example:

int i = 3;
printf("i = %d\n", i);
i ++;

Moving the increment before the printf, causing it to print "i = 4",
would be non-conforming. I believe the transformation you describe
would be non-conforming for the same reasons.

But I'm not 100% certain that I'm correct about this, and I could
probably shoot some holes in my own arguments if I put my mind to it.

As a programmer, I'll just avoid things like "i = f(++i);". If I were
implementing a compiler, I'd try to be conservative enough in my
optimizations so that "i = f(++i);" works as expected, even if I can
justify breaking it by invoking undefined behavior if I squint while
reading the standard.

The real question is, if an implementation does something other than
the obvious for "i = f(++i);", can I complain about non-conformance in
my bug report? I *think* the answer is yes.
 
C

CBFalconer

Keith said:
.... snip ...

The real question is, if an implementation does something other
than the obvious for "i = f(++i);", can I complain about non-
conformance in my bug report? I *think* the answer is yes.

And who would ever write that when "i = f(i + 1);" works without
any question, and is even clear to the reader? ++ and -- are
overused IMO.
 
S

S.Tobias

Keith Thompson said:
Eric Sosman said:
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
[snip]

The whole purpose of sequence points, I think, is to impose a
reasonable set of of restrictions on what optimizations a compiler is
allowed to perform. An optimizer *can* move a side effect across a
sequence point, but it's allowed to do so only if it doesn't destroy
the semantics of the program. The actual program needs to behave, [snip]

As a programmer, I'll just avoid things like "i = f(++i);". If I were
implementing a compiler, I'd try to be conservative enough in my
optimizations so that "i = f(++i);" works as expected, even if I can
[snip]

I think there's a worse pit-fall:
int i=0;
a = f(i++);
Which element is being set?
I think this is unspecified (6.5.16#4), but the behaviour
is defined.

Here's a test program and results:

gcc: 1 0
como: 0 1



#include <stdio.h>

int one(int unused) { return 1; }

int main()
{
int a[2] = {0};
int i = 0;
a = one(i++);
printf("%d %d\n", a[0], a[1]);
return 0;
}
 
P

pete

CBFalconer said:
And who would ever write that when "i = f(i + 1);" works without
any question, and is even clear to the reader? ++ and -- are
overused IMO.

For style reasons, I don't like arguments with side effects.
 
B

Barry Schwarz

Keith Thompson said:
Eric Sosman said:
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
[snip]

The whole purpose of sequence points, I think, is to impose a
reasonable set of of restrictions on what optimizations a compiler is
allowed to perform. An optimizer *can* move a side effect across a
sequence point, but it's allowed to do so only if it doesn't destroy
the semantics of the program. The actual program needs to behave, [snip]

As a programmer, I'll just avoid things like "i = f(++i);". If I were
implementing a compiler, I'd try to be conservative enough in my
optimizations so that "i = f(++i);" works as expected, even if I can
[snip]

I think there's a worse pit-fall:
int i=0;
a = f(i++);
Which element is being set?
I think this is unspecified (6.5.16#4), but the behaviour
is defined.


I don't think the behavior is defined. While i is being updated only
once, there is a second requirement that i be evaluated at most once
as part of the process. Here i is being evaluated twice.
Here's a test program and results:

gcc: 1 0
como: 0 1



#include <stdio.h>

int one(int unused) { return 1; }

int main()
{
int a[2] = {0};
int i = 0;
a = one(i++);
printf("%d %d\n", a[0], a[1]);
return 0;
}


The fact that undefined behavior appears to work as expected doesn't
make the behavior defined.



<<Remove the del for email>>
 
R

Robert Gamble

Barry said:
Keith Thompson said:
[...]
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point [snip]

The whole purpose of sequence points, I think, is to impose a
reasonable set of of restrictions on what optimizations a compiler is
allowed to perform. An optimizer *can* move a side effect across a
sequence point, but it's allowed to do so only if it doesn't destroy
the semantics of the program. The actual program needs to behave, [snip]

As a programmer, I'll just avoid things like "i = f(++i);". If I were
implementing a compiler, I'd try to be conservative enough in my
optimizations so that "i = f(++i);" works as expected, even if I can
[snip]

I think there's a worse pit-fall:
int i=0;
a = f(i++);
Which element is being set?
I think this is unspecified (6.5.16#4), but the behaviour
is defined.


I don't think the behavior is defined. While i is being updated only
once, there is a second requirement that i be evaluated at most once
as part of the process. Here i is being evaluated twice.


That would mean that the expression "i = i + i"; is undefind as i is
evaluated more than once. The second requirement you speak of actually
states that "the prior value shall be read only to determine the value
to be stored". The example still doesn't meet this requirement though
as it is not guaranteed that i won't be both incremented and evaluated
before the call to f and the evaluation of i in a is not being read
to "determine the value to be stored" into i.
Here's a test program and results:

gcc: 1 0
como: 0 1



#include <stdio.h>

int one(int unused) { return 1; }

int main()
{
int a[2] = {0};
int i = 0;
a = one(i++);
printf("%d %d\n", a[0], a[1]);
return 0;
}


The fact that undefined behavior appears to work as expected doesn't
make the behavior defined.


I don't think Stan was implying that it did, just stating that he
thought it was unspecified and providing an example to demonstrate the
danger of the scenerio.

I could be argued that whether or not the behavior is undefined is
unspecifed since the order of evaluation that could make the behavior
undefined is itself unspecified.

Robert Gamble
 
B

Barry Schwarz

Barry said:
[...]
Personally, I've never been entirely convinced that
`i = f(++i)' is bulletproof. Yes, there's a sequence point
[snip]

The whole purpose of sequence points, I think, is to impose a
reasonable set of of restrictions on what optimizations a compiler is
allowed to perform. An optimizer *can* move a side effect across a
sequence point, but it's allowed to do so only if it doesn't destroy
the semantics of the program. The actual program needs to behave,
[snip]

As a programmer, I'll just avoid things like "i = f(++i);". If I were
implementing a compiler, I'd try to be conservative enough in my
optimizations so that "i = f(++i);" works as expected, even if I can
[snip]

I think there's a worse pit-fall:
int i=0;
a = f(i++);
Which element is being set?
I think this is unspecified (6.5.16#4), but the behaviour
is defined.


I don't think the behavior is defined. While i is being updated only
once, there is a second requirement that i be evaluated at most once
as part of the process. Here i is being evaluated twice.


That would mean that the expression "i = i + i"; is undefind as i is
evaluated more than once. The second requirement you speak of actually


I don't think the i on the left of the = operator is evaluated. If it
were, then a sequence like
int i;
i = 0;
would evaluate the uninitialized i which is another example of
undefined behavior.




<<Remove the del for email>>
 

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,167
Messages
2,570,911
Members
47,453
Latest member
MadelinePh

Latest Threads

Top