0

ISO/IEC 9899(TC2) §6.5 — 2 Expressions tells us:

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.

This is what I kept in mind, remembered and would have told anyone asking me why the line from the title gives unexpected outputs.

But today I just discovered this line:

§7.19.6 — 1 Formatted input/output functions:

The formatted input/output functions shall behave as if there is a sequence point after the actions associated with each specifier.

Which made me assume:

While

int i = 0;
printf ("%d, %d", ++i, i++);

should be undefined, the next example should be ok by the mentioned clause:

int i = 0;
printf ("%d, %d, %d", ++i, i, i++);

But the output is:

2, 2, 0

I never had seen a better example for this indicating undefined behavior.

But why? If the clause is true

"[...]behave as if there is a sequence point after the actions associated with each specifier."

then applying the rule under §6.5 — 2 onto each of the actiosn associated with the specifier doesn't let us cross that rule as in:

(SP representing a relevant sequence point)

SP1++i SP2iSP3i++

From SP1 in the given range between the previous and the next SP, ++i is the only modification of i's stored value.

From SP2 what is in the range between the previous and the next SP is: ++i and i where ++i still is the only modification of that value.

If we now take SP3 all that is going on between the previous SP (SP2) and the next SP(end of the invocation) is:

i and i++ still just a single modification of i in the whole range between the previous and the next SP.

So what am I interpreting here wrong about the way sequence points work?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
dhein
  • 6,431
  • 4
  • 42
  • 74
  • Feel free to downvote it if you think it is appropiate. But note this isn't just another homwork question. I encountered something that seemed weird to me, I did quiet some decent research about it and made a clear point of my confusion and why it is diferent from other questions about this. If you still think I missed something or did not make enough effort, please let me atleast know why you think so. – dhein Jul 27 '16 at 11:43

1 Answers1

2

The issue isn't with the actions associated with the specifiers. The issue is with the calculation of the parameters to the printf function which is finished before the first specifier performs any actions.

Imagine if the code was:

void foo (int i1, int i2, int i3)
{
     printf("%d, %d, %d", i1, i2, i2);
}

foo (++i, i, i++);

Here it would be quite clear. And the wrapper doesn't change anything.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Can you elaborate for what cases those sequence points between the assoicated actions are even relevant for if that is the case? Or should I ask that as seperate question? – dhein Jul 27 '16 at 11:37
  • It should be obvious for things like `scanf`. There might be some format specifiers where it could make a different for `printf`, but I can't think of any. Maybe someone else can. – David Schwartz Jul 27 '16 at 11:39
  • 2
    Specifically the format specifier `%n` which writes the number of chars printed so far into the given address. – P.P Jul 27 '16 at 11:42
  • Can you also make clear in your answer, if my understanding was right and the problem isn't that this is undefined behavior, as the output `2, 2, 0` let me assume, but as this is a expectable well defined outcome due to printf just beeing a wrapper to the varg c functions, or if this is undefined behavior? As your last edit adding the snippet made me unsure about this. – dhein Jul 27 '16 at 11:45
  • @Zaibis It's UB. You broke the rule you cited in your first quote. There's no right thing for it to do. There's no reason to expect `2, 2, 0`. Why would you expect the parameters to be calculated in that order as opposed to some other order? – David Schwartz Jul 27 '16 at 11:47
  • @DavidSchwartz: that I wasn't sure about and as you didn't make it clear, I asked you, before breaking my had on to the "why?". So the second rule I cited is just related to operations directly "performed by the specifier" as @P.P. 's example of `%n` would modify a value. So one could do `printf("te%dst%n", i, &(x++));`? – dhein Jul 27 '16 at 11:55
  • @Zaibis Yes. And the `x++` would take place before the `%d` or `%n`. – David Schwartz Jul 27 '16 at 12:37
  • Because of the `()` or even without them? Wow... so much new I learned today '^.^ Anyway I still would welcome it seeing it added itno your answer. – dhein Jul 27 '16 at 12:40
  • 1
    @Zaibis Just because a function's arguments have to be computed before the function can be called. – David Schwartz Jul 27 '16 at 12:44