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.