home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / lang / c / 16220 < prev    next >
Encoding:
Text File  |  1992-11-09  |  7.9 KB  |  206 lines

  1. Newsgroups: comp.lang.c
  2. Path: sparky!uunet!snorkelwacker.mit.edu!bloom-picayune.mit.edu!news
  3. From: scs@adam.mit.edu (Steve Summit)
  4. Subject: retrospective on evaluation order
  5. Message-ID: <1992Nov9.173126.11069@athena.mit.edu>
  6. Sender: news@athena.mit.edu (News system)
  7. Nntp-Posting-Host: adam.mit.edu
  8. Organization: none, at the moment
  9. References: <josef.720691377@uranium> <1dau8nINNk59@uranium.sto.pdb.sni.de> <qBak-G@ppcger.ppc.sub.org>
  10. Date: Mon, 9 Nov 1992 17:31:26 GMT
  11. Lines: 193
  12.  
  13. In article <josef.720691377@uranium>, Josef Moellers <mollers.pad@sni.de> writes:
  14. > Furthermore, as "boolean" operations are nothing special in C, I wonder
  15. > whether in
  16. >     a() || b()
  17. > b _should_ be called if a() returns TRUE.
  18.  
  19. I'd like to thank Josef for asking this question, which in the
  20. surrounding context has crystallized the question and
  21. helped me for the first time to understand it perfectly clearly.
  22.  
  23. I do understand evaluation order well enough to write C programs
  24. correctly.  But there has always been something missing in my
  25. understanding of other people's misunderstandings.  Josef's
  26. question is actually on the FAQ list (question 4.3 in the latest
  27. versions), but it was put there at (as I recall) Karl Heuer's
  28. request -- I didn't really see (until now) what it added.  (I
  29. think I'll revise section 4 again, to make things even clearer.)
  30.  
  31. Years ago, a colleague scoffed at some third person's
  32. misunderstanding of evaluation order with the comment, "Don't
  33. they understand the difference between precedence and evaluation
  34. order?"  I agreed and nodded knowingly, but that comment has
  35. always bothered me as well, because the concept of precedence is
  36. not wholly divorced from or orthogonal to evaluation order.
  37.  
  38. "Precedence" tells you, in the absence of explicit or overriding
  39. parentheses, how an expression is grouped; it determines the
  40. shape of the "parse tree" corresponding to a given expression.
  41. This grouping (i.e. the shape of the parse tree) *does* impose
  42. some partial ordering on the evaluations which will take place:
  43. obviously, you must evaluate the operands of an operator before
  44. applying the operator (actually, even here there are exceptions
  45. with respect to the "short-circuiting" operators, which is what
  46. Josef is asking about).
  47.  
  48. Therefore, given the expression
  49.  
  50.     a + b * c
  51.  
  52. , precedence tells us that the multiplication must happen before
  53. the addition, as we learned back in Algebra.  In the expression
  54.  
  55.     d + e + f
  56.  
  57. , precedence tells us nothing, although associativity tells us
  58. that d should be added to e and the sum added to f, i.e. that it
  59. should be evaluated as
  60.  
  61.     (d + e) + f
  62.  
  63.  .
  64.  
  65. So far I seem to be making the wrong point: in these simple
  66. examples, precedence and associativity have almost completely
  67. determined the "evaluation order," that is, the order in which
  68. the multiplications and additions should take place.  This is
  69. because, in these simple examples, the partial ordering implied
  70. by precedence and associativity has apparently been sufficient to
  71. order the entire expression.  As soon as we begin to consider
  72. side effects, however, we will see that partial ordering does not
  73. tell the whole story.
  74.  
  75. Let us look at
  76.  
  77.     g() + h() * j()
  78.  
  79.  .  As before, the multiplication must happen before the
  80. addition, but what does that tell us about the order in which the
  81. three functions are called?  Not much.  h() and j() must be
  82. called before the multiplication can take place, and all three
  83. before the addition can take place, but that still leaves many
  84. orders in which the calls could be interleaved.  In particular,
  85. nothing says that we need call h() and j(), then perform the
  86. multiplication, then call g(), then do the addition; g() could
  87. easily be called first.  (To answer another question, we do know
  88. that all three functions must in principle be called; there is no
  89. short-circuiting implied by things like multiplication by 0.)
  90.  
  91. Once we begin considering side effects, we find that complete
  92. evaluation order is unspecified even for simpler expressions,
  93. such as
  94.  
  95.     k() + l()
  96.  
  97. and
  98.  
  99.     volatile int m, n;
  100.     int p = m + n;
  101.  
  102. We don't know whether k() or l() will be called first, and we
  103. don't know whether the variables m or n will be *fetched* first
  104. (and, since they are volatile, presumably this may make a
  105. difference to us).
  106.  
  107. We can now turn to a couple of final points, the first of
  108. historical interest only.  K&R ("Classic") C gave a compiler
  109. license to rearrange associative operators.  That is, given
  110.  
  111.     d + e + f
  112.  
  113. , the compiler might emit code to add e to f first, or even d to f.
  114. This license has been revoked under ANSI C; compilers must appear
  115. to honor the prescribed associativity as it applies to evaluation
  116. order (although the "as if" rule still allows rearrangements if
  117. they are guaranteed not to make a difference).  The difference
  118. (between being able to rearrange at will and being able to
  119. rearrange only if it can't make a difference) is subtle, but can
  120. be important in numerical work and when things like overflow are
  121. possible.
  122.  
  123. Secondly, what about those "short-circuiting" operators?  Given
  124.  
  125.     k() + l()
  126.  
  127. , we don't know what order the functions will be called in.
  128. However, given
  129.  
  130.     q() || r()
  131.  
  132. , we *are* guaranteed that q() will be called first, and
  133. furthermore that r() will not be called at all if q() returns
  134. nonzero (i.e. "true").  Given
  135.  
  136.     s() && t()
  137.  
  138. , we are similarly guaranteed that s() will be called first, and
  139. that t() will not be called if s() returns zero ("false").  Given
  140.  
  141.     u() ? w() : x()
  142.  
  143. , we are guaranteed that u() will be called first, and that only
  144. one of w() and x() will be called.  Finally, given
  145.  
  146.     y(), z()
  147.  
  148. , we are guaranteed that y() will be called first, then z().
  149.  
  150. To summarize: precedence and associativity impose a partial
  151. ordering on some of the operations which take place during the
  152. evaluation of a full expression, but they are in general
  153. insufficient to completely determine the order, particularly when
  154. side effects are considered.  The four operators ||, &&, ?:, and
  155. , (comma) do impose constraints on the order in which their
  156. respective operands are evaluated, although order of evaluation
  157. within those operands, and order of operation within the larger
  158. expression containing the ||, &&, ?:, or , operator, may still
  159. have unspecified aspects.
  160.  
  161.                     Steve Summit
  162.                     scs@adam.mit.edu
  163.  
  164. P.S.  In this article, I have deliberately been casual rather
  165. than rigorous in my explanations.  (In particular, I have freely
  166. used the term "precedence" which Chris correctly points out can
  167. be superfluous as well as misleading.)  I have glossed over a few
  168. fine points; more formal explanations can be found in any number
  169. of nearby articles under the Subject: order of evaluation.  This
  170. article doesn't really say anything that other correct answers
  171. have not; the entire issue is summed up by a single sentence in
  172. section 3.3 of the ANSI C Standard [X3.159-1989, p. 39]:
  173.  
  174.     Except as indicated by the syntax or otherwise specified
  175.     later (for the function-call operator (), &&, ||, ?:, and
  176.     comma operators), the order of evaluation of
  177.     subexpressions and the order in which side effects take
  178.     place are both unspecified.
  179.  
  180. In the interest of some rigor, I should make one other point:  In
  181. the expression
  182.  
  183.     e1 + e2
  184.  
  185. , we said that e1 and e2 must be evaluated before the addition
  186. can take place.  This is still, in two respects, a partial
  187. ordering: not only do we not know whether e1 or e2 is evaluated
  188. first; we cannot even assume that one of them is evaluated first.
  189. There is no "sequence point" implied by an addition operator, so
  190. we had best assume that the evaluations of e1 and e2 happen at
  191. the same time; this is why we must rule out expressions such as
  192.  
  193.     i++ + i++
  194.  
  195. , the behavior of which is undefined, and is *not* guaranteed to
  196. be the same as
  197.  
  198.     tmp1 = i++, tmp2 = i++, tmp1 + tmp2
  199.  
  200.  .  In other words, the commutativity of addition does *not* mean
  201. that it doesn't make a difference which increment happens first.
  202. See the FAQ list, question 4.2, or recall one of Chris' colorful
  203. descriptions of a compiler's throwing several values up into the
  204. air and not catching them reliably until the dust settles at the
  205. next sequence point.
  206.