2

I read this in the book C programming : A modern approach -

According to C standard statements such as

 c = (b=a+2) - (a=1) ;

causes undefined behavior.

There is no reference to why however. My understanding is this :

  1. All the variables have been modified only once between the sequence points. (So shouldn't be UB)

  2. The order of evaluation of sub expressions is not defined. (But that doesn't mean it invokes undefined behavior, right ?)

What else is causing it to be undefined behavior ?

Amit Tomar
  • 4,800
  • 6
  • 50
  • 83
  • 1
    >>> when is a assigned? – Mitch Wheat Aug 08 '14 at 07:20
  • 1
    There's a separate rule that "the prior value shall be accessed only to determine the value to be stored"; you are accessing `a` but not to determine the value to be stored into `a`. – T.C. Aug 08 '14 at 07:21
  • Maybe C11 is slightly different here from C99. In which standard are you interested? – mafso Aug 08 '14 at 07:22
  • I voted to close as a duplicate; this case is covered well by the second answer to that question (it's equivalent in undefinedness to `a[i] = i++;`). – Oliver Charlesworth Aug 08 '14 at 07:44

4 Answers4

2

1 and 2 are perfectly correct. In the case of order of evaluation of operands, it is unspecified for most operators in C. Meaning that either (b=a+2) or (a=1) can get evaluated first, and you cannot know which order that applies for any given case.

In addition, if a variable is modified between two sequence points, any other access to that variable is not allowed, save for calculating what value to store in it.

C99 states this in 6.5 (emphasis mine):

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

So code like a = a+1 is perfectly well-defined, while code like a = a++ leads to undefined behavior.

It all boils down to the "abstract machine" which is the rules determining the execution order of your program. Writing a value to a variable is a side effect and the C standard states that all side effects must have occurred before the next sequence point. Now if you have several side effects related to the same variable, there are no guarantees in which order they will be sequenced in relation to each other, until the next sequence point is reached.

The practical advise to avoid bugs caused by sequencing and order of evaluation, is to keep expressions simple, with as few operators and as few side effects on each line as possible. In the case of your original example, a better way to write the code would be:

b = a + 2;
a = 1;
c = b - a;

The above code cannot be misinterpreted neither by the compiler nor by the human reader.


Just for the record, C11 has different text, but the very same meaning:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

And 3. reading a value that's modified can only happen as a part of calculating that new value.

The reading of a in b=a+2 is independent of the writing in a=1 thus UB

sp2danny
  • 7,488
  • 3
  • 31
  • 53
0

If an evaluation of a variable and a modification of the same variable are unsequenced, then the behavior is undefined.

You might expect to read either the new value or the old value, but variables in C are not atomic in that way. You could end up reading the variable in mid-update, or the compiler could screw up something else because the variable's assumed value changes in mid-optimization when the order of evaluation changes.

In general, code which is nondeterministic might as well be nonsense.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Can I always take `NonDeterminism --> Undefined Behavior` ? – Amit Tomar Aug 08 '14 at 07:26
  • 1
    @AmitTomar: Not necessarily. You cannot tell the order of threads and when they're going to be interrupted by another. Still, this is well-defined, as long as you take care of data races (and deadlocks). But non-determinism in a single expressions? Usually yes. – Zeta Aug 08 '14 at 07:38
  • @AmitTomar: It's the other way round; the compiler doesn't define a behaviour, therefore the compiler is free to do what it wants; one outcome might be non-deterministic behaviour. – Oliver Charlesworth Aug 08 '14 at 07:41
  • @Zeta Does that statement hold for evaluating expressions at least ? I mean if I find some sort of nonDeterminism while calculating the sub-expressions, can I declare it UB ? – Amit Tomar Aug 08 '14 at 07:41
  • @AmitTomar: Yes: *"If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings."* (C11, 6.5.2) – Zeta Aug 08 '14 at 07:53
  • Any particular compiler will always, always give the same result. It may even be what you want. The problem with undefined behaviours is the same source-code on a different compiler MAY give a different result. It would take an expert in both compilers , and they are rare people, to pinpoint the *bug* , work out what the original coder intended, and edit a correction. /expensive for all concerned/ – Arif Burhan Mar 04 '16 at 19:33
0

If run on an MIMD CPU, it's possible that the (b=a+2) is handled by one core, while the a=1 handled by another, leading the a being written and read at the same time, causing a protection fault. (This requires a compile designed to generate multiprocessing code, which general-purpose compiler don't do, but such compiler do exist for niche markets)

James Curran
  • 101,701
  • 37
  • 181
  • 258