Zaibis Zaibis - 1 year ago 143
C Question

Why printf ("%d%d%d", ++i, i, i++) is undefined behavior?

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:


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)


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

From SP2 what is in the range between the previous and the next SP is:
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:

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

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

Answer Source

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.