[comp.lang.c] Possible C Anomaly

gamma@EDN-UNIX.arpa (W. J. Showalter) (09/11/87)

I would like to get some comments on the following problem involving
what I consider an inconsistency in the C programming language.

Consider the statment

	z = (a > b) ? a : b;

which is taken directly from Kernighan and Ritchie on page 47.  According
to them the statement is equivalent to

if (a > b)
   z = a;
else
   z = b;


The other day I wrote the following code:
			    k = 10;
				.
				.
				.
				.
/* Example #1 */            k = (k > 1) ? k-- : 1;

My intent was to decrement k with each pass until it reached 1 and then to
keep it at one.  There are a variety of ways to accomplish this but I just
happened to try this one first.

According to K&R, the above expression is equivalent to

/* Example #2 */

if (k > 1)
   k = k--;
else
   k = 1;

Note:  In this case the else is not needed but included for
completeness.

The problem is is that the two examples behave differently.  In the first
example k does not change values.  It remains equal to 10 indefinitely.

In the second example k does change like one would expect.

I generated assembler listings for the two examples and discovered that
example 1 makes used of an interim register to house the value of k
BEFORE decrementing.  It eventually moves this value back to k, replacing
the decremented value with the original.

I would like to get some other opinions on this if anyone has the time
or interest.

Also, although it seems to me that there is a slight inconsistency within
the language I tried 3 different compilers on 3 different machine
architectures and they all handle it the same way.  I assume that there
must be some well known and accepted BNF which is used to arrive at this
consistency between the different compilers. 

Jim

gamma@edn-unix.arpa

crowl@cs.rochester.edu (Lawrence Crowl) (09/12/87)

In article <9236@brl-adm.ARPA> gamma@EDN-UNIX.arpa (W. J. Showalter) writes:
]
]/* Example #1 */            k = (k > 1) ? k-- : 1;
]
]/* Example #2 */            if (k > 1) k = k--;
]                                  else k = 1;
]
]The problem is is that the two examples behave differently.  In the first
]example k does not change values.  It remains equal to 10 indefinitely.
]
]In the second example k does change like one would expect.
]
]I generated assembler listings for the two examples and discovered that
]example 1 makes used of an interim register to house the value of k
]BEFORE decrementing.  It eventually moves this value back to k, replacing
]the decremented value with the original.

The order of argument evaluation within expressions is undefined in C.  The
order of expression evaluation only matters when the expression has side
effects on other parts of the expression, as yours does.  Because of the
undefined order, the compiler may choose to execute either k = k or k-- first.
In the first example, it decrements k first and then assigns the old value of
k to k.  In the second example, it assigns the old value of k to k and then
decrements k.  In short, the code is wrong, not the compilers.
-- 
  Lawrence Crowl		716-275-9499	University of Rochester
		     crowl@cs.rochester.arpa	Computer Science Department
 ...!{allegra,decvax,seismo}!rochester!crowl	Rochester, New York,  14627

gwyn@brl-smoke.UUCP (09/12/87)

In article <9236@brl-adm.ARPA> gamma@EDN-UNIX.arpa (W. J. Showalter) writes:
>   k = k--;

Your real problem is the assumption that this is a meaningful thing to do.
(Are you really trying to keep k unchanged in such a complicated way?)
You need to watch out for these "this construct is equivalent to" claims;
usually there are subtle inequivalencies.  For example, one you haven't
noticed concerns the way different types are combined in producing the
result of a ?: expression.

>... I tried 3 different compilers on 3 different machine
>architectures and they all handle it the same way.

Most PCC-based compilers probably would produce the same result,
but that's "just an accident" -- it's not guaranteed by the language
specification.

chris@mimsy.UUCP (Chris Torek) (09/13/87)

In article <9236@brl-adm.ARPA> gamma@EDN-UNIX.arpa (W. J. Showalter) writes:
>	k = (k > 1) ? k-- : 1;
>... According to K&R, the above expression is equivalent to
>	if (k > 1)
>		k = k--;
>	else
>		k = 1;
>... The problem is is that the two examples behave differently.

This is a case of `you are not supposed to do that!'.  The two
expressions are equivalent, and the second form is not defined:

	k = k--;

like

	a[i] = i--;

is simply not defined in C.  Since the second form is not defined,
neither is the first.

A really good compiler would complain.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

ftw@datacube.UUCP (09/14/87)

You should look at dpANS, in particular the discussion of "sequence points".
That is why your example does not work (it is incorrect code!).  When you
write:

	k = k--;

the compiler is free to decrement k FIRST 8-o


				Farrell T. Woods 

Datacube Inc. Systems / Software Group	4 Dearborn Rd. Peabody, Ma 01960
VOICE:	617-535-6644;	FAX: (617) 535-5643;  TWX: (710) 347-0125
UUCP:	ftw@datacube.COM,  ihnp4!datacube!ftw
	{seismo,cbosgd,cuae2,mit-eddie}!mirror!datacube!ftw

mrd@sun.mcs.clarkson.EDU (Michael R. DeCorte) (09/14/87)

		   .
   /* Example #1 */            k = (k > 1) ? k-- : 1;

   My intent was to decrement k with each pass until it reached 1 and then to
   keep it at one.  There are a variety of ways to accomplish this but I just
   happened to try this one first.

   The problem is is that the two examples behave differently.  In the first
   example k does not change values.  It remains equal to 10 indefinitely.

Try 
	k = (k > 1) ? --k : 1;
or
	k = (k > 1) ? (k - 1) : 1;

from page 42 of KR

"But the expression ++n increments n before using its value, while n++
increments n after its values has been used."

You will also notice the chart on page 49 in section 2.12 Precedence
and Order of Evaluation

for the expression 
	k = (k > 1) ? k-- : 1;
	1    2        3

the compiler evaluates the  k(3) yielding an rvalue. It then decrements
the lvalue k.  It then assign to the lvalue k(1) the rvalue generated
by k(3).  As k(3) was equal to k you have an infinite loop.  This
what is supposed to happen. 

Michael DeCorte
mrd@clutx.clarkson.edu

mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) (09/16/87)

In article <9270@brl-adm.ARPA> mrd@sun.mcs.clarkson.EDU (Michael R. DeCorte) writes:
<   /* Example #1 */            k = (k > 1) ? k-- : 1;
<
<   My intent was to decrement k with each pass until it reached 1 and then to
<   keep it at one.  There are a variety of ways to accomplish this but I just
<   happened to try this one first.

<Try 
<	k = (k > 1) ? --k : 1;

No! Consider that you're doing (one way) "k = --k". You still don't know
whether k will be decremented or assigned to first.

Even worse, this statement doesn't make a lot of sense. Try doing
another "equivalent" replacement, and get

	k = (k = k - 1)

Why would you want to write something like that?

<or
<	k = (k > 1) ? (k - 1) : 1;

Much better - it's right.

I've noticed that may C programmers overuse the "++" and "--"
operators. They're nice for changing the value of a variable by one
when you want to use the current/resulting value in another
expression. If you just want the decremented value, write "k - 1". You
can avoid stepping on your own toes that way. Likewise, if you really
don't want the value for later use, you might consider "k -= 1"
instead of "k--" or "--k".

Since this comes up at regular intervals, I think I'll repeat it here:

C *does not* guarantee the order of evaluation of an expressions,
except for the logical operators. Any expression that changes the
value of a variable more than once should be looked at with extreme
caution. If logicals don't force order of evaluation, then the
resulting code is *not* portable.

	<mike
--
Lather was thirty years old today,			Mike Meyer
They took away all of his toys.				mwm@berkeley.edu
His mother sent newspaper clippings to him,		ucbvax!mwm
About his old friends who'd stopped being boys.		mwm@ucbjade.BITNET

palmer@hsi.UUCP (Mike Palmer) (09/17/87)

In article <5112@jade.BERKELEY.EDU>, mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) writes:
> In article <9270@brl-adm.ARPA> mrd@sun.mcs.clarkson.EDU (Michael R. DeCorte) writes:
> <   /* Example #1 */            k = (k > 1) ? k-- : 1;
> <
> <   My intent was to decrement k with each pass until it reached 1 and then to
> <   keep it at one.  There are a variety of ways to accomplish this but I just
> <   happened to try this one first.
> 
> <Try 
> <	k = (k > 1) ? --k : 1;
> 
> No! Consider that you're doing (one way) "k = --k". You still don't know

... stuff deleted ...

> <or
> <	k = (k > 1) ? (k - 1) : 1;
> 
> Much better - it's right.

why not just use

	(k > 1) ? k-- : (k = 1);

or better yet

	(k > 1) ? k-- :;


just my two cents.


-- 
	Mike Palmer			{uunet,ihnp4,yale}!hsi!palmer
	Health Systems International
	New Haven, CT  06511
	USA

mwm@eris.UUCP (09/20/87)

In article <682@hsi.UUCP> palmer@hsi.UUCP (Mike Palmer) writes:
<In article <5112@jade.BERKELEY.EDU>, mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) writes:
<> <Try 
<> <	k = (k > 1) ? --k : 1;
<> 
<> No! Consider that you're doing (one way) "k = --k". You still don't know
<
<... stuff deleted ...
<
<> <or
<> <	k = (k > 1) ? (k - 1) : 1;
<> 
<> Much better - it's right.
<
<why not just use
<
<	(k > 1) ? k-- : (k = 1);

Because the original question was "why doesn't k = ..." work. Also,
this displays the bad habit of using something normally used to
generate an expression as a statement.


<or better yet
<
<	(k > 1) ? k-- :;

Because it's not legal C. Gotta have an expression between the : and
the ;. 

If you're going to rewrite the original statement

	"k = (k > 1) ? --k : 1 ;"
	
you'd be better of just to go to

	if (k > 1) k -= 1 ;

if it will work - the semantics aren't quite the same.

	<mike
--
But I'll survive, no you won't catch me,		Mike Meyer
I'll resist the urge that is tempting me,		ucbvax!mwm
I'll avert my eyes, keep you off my knee,		mwm@berkeley.edu
But it feels so good when you talk to me.		mwm@ucbjade.BITNET

dg@wrs.UUCP (09/25/87)

In article <682@hsi.UUCP> palmer@hsi.UUCP (Mike Palmer) writes:
]In article <5112@jade.BERKELEY.EDU>, mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) writes:
]] In article <9270@brl-adm.ARPA> mrd@sun.mcs.clarkson.EDU (Michael R. DeCorte) writes:
]] <   /* Example #1 */            k = (k > 1) ? k-- : 1;
]] <
]] <   My intent was to decrement k with each pass until it reached 1 and then to
]] <   keep it at one.  There are a variety of ways to accomplish this but I just
]] <   happened to try this one first.
]] 
]] <Try 
]] <	k = (k > 1) ? --k : 1;
]] 
]] No! Consider that you're doing (one way) "k = --k". You still don't know
]
]... stuff deleted ...
]
]] <or
]] <	k = (k > 1) ? (k - 1) : 1;
]] 
]] Much better - it's right.
]
]why not just use
]
]	(k > 1) ? k-- : (k = 1);
]
]or better yet
]
]	(k > 1) ? k-- :;
]
]
]just my two cents.

I've got my asbestos suit ready for the incoming flames, so I think I can
safely ask "Why the addictive fixation on the -- operator for this case.
Why not just say:

	k = (k > 1) ? k - 1 : 1;

Lint likes it, there is no problem with order of evaluation etc. etc. etc.
Alternatively why not try:

	if (k > 1)
	  k--;

Which is about as obvious an answer to what is required.
--
		dg@wrs.UUCP - David Goodenough

		..... ihnp4!sun!decwrl!ucbvax!dual!wrs!dg

					+---+
					| +-+-+
					+-+-+ |
					  +---+

mrd@clutx.clarkson.EDU (Michael R. DeCorte) (10/03/87)

I missed the comment 

]] No! Consider that you're doing (one way) "k = --k". You still don't know 

so I might simply missing something but I still believe this is
correct.  It does not matter which is evaluted first.  k will be
assigned whatever the rvalue is and the rvalue of --k is the same as
(k-1).  Therefore in this context --k is the same as (k-1).

Now I will admit that this is not the nicest way of doing this and
will cause problems with anything much more complicated but I wanted
to modify the the statement as given by the original author
little as possible.

one of the possible solutions given by Mike Palmer was
]    (k > 1) ? k-- : (k = 1);
This is correct but I don't care for the style simply because
I view ? to be an expression but it doesn't really matter.

The other possibility given by Mike Palmer was
]    (k > 1) ? k-- :;
This is not really correct.  What if k <= 0?  Now you could argue
that this will never happen but it is not what the original statement
was.


David Goodenough offered two solutions.  The first was identical to
one of mine but second solution was
>    if (k > 1)
>      k--;
the problem with this is the same as Mike Palmer's.  What if k <=0?

The way I would actully code it is rather non C'ish
if (k > 1)
{
	k--;
}
else
{
	k = 1;
}
  
And to give yet another solution you could also try

if (k-- <= 1)
{
	k = 1;
}

or
    (k-- <= 1) ? k = 1 : ;

Although not very readable this has the advantage that it will very
good with many cpu's and compilers but very bad with others.

--------------------------------------------------------------
All opinions and/or comments stated here are my own and are not
in any way related to Clarkson U.

Michael DeCorte
mrd@clutx.clarkson.edu

karl@haddock.ISC.COM (Karl Heuer) (10/05/87)

In article <9601@brl-adm.ARPA> mrd@clutx.clarkson.EDU (Michael R. DeCorte) writes:
>I might simply missing something but I still believe [ k = --k ] is
>correct.  It does not matter which is evaluted first.

It is incorrect.  The value of the expression is (oldk - 1), with TWO side
effects (decrement k, and store (oldk - 1) in k).  It would be legal for the
compiler to perform the store and then the decrement, leaving (oldk - 2) in k.

>The way I would actully code it is rather non C'ish
>if (k > 1) k--; else k = 1;

By "non C'ish" you seem to mean that it isn't obscure enough?  I think it's
both C'ish and the best way to write it if you want to preserve those exact
semantics.  But I'd just write "if (k > 1) --k;", if it's known that k > 0 (as
was implied, I think, by the original problem statement).

>And to give yet another solution you could also try [...] or
>    (k-- <= 1) ? k = 1 : ;
>Although not very readable this has the advantage that it will very
>good with many cpu's and compilers but very bad with others.

As has been pointed out already, this one is illegal; you have to either use a
dummy expression (": 0;") or use the if-else syntax (which is better anyway,
if you aren't in an expression context).

As for being "very good with many CPUs/compilers", I think you'd do better
with "if (--k <= 0) k=1;" which has the advantages of using a predecrement (so
no need to save the old value) and a compare against zero (which is often a
side-effect of decrementing anyway).

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

peter@sugar.UUCP (Peter da Silva) (10/08/87)

> k = (k>1) ? k-- : 1;

What's wrong with the following?

k = (k > 1) ? (k - 1) : 1;

It's the only reasonable interpretation I can make of the expression anyway.
Nobody says you HAVE to use -- just because it's there.
-- 
-- Peter da Silva  `-_-'  ...!hoptoad!academ!uhnix1!sugar!peter
-- Disclaimer: These U aren't mere opinions... these are *values*.

Terry_L_Parker@cup.portal.com (10/13/87)

The difference between k-- and k-1 may be in the resulting code the
compiler creates.  k-- would automatically create a DEC <reg> type of
instruction while k-1 would create a SUB <reg>,<value>  where "reg" is
a register (or memory address) and "value" would be 1.  Though I would
think a compiler that does a decent job of optimization would automatically
turn any SUB ,1 's  into DEC's, but you never know.