Sequence points in printf

I read here that there is a sequence point:

After the action associated with the input / output conversion format specifier. For example, the expression printf("foo %n %d", &a, 42) has a sequence point after evaluating %n before printing 42 .

However, when I run this code :

 int your_function(int a, int b) { return a - b; } int main(void) { int i = 10; printf("%d - %d - %d\n", i, your_function(++i, ++i), i); } 

Instead of what I expect, I get:

12 - 0 - 12

This means that there was no sequence point created for the transform format specifier. Is http://en.wikipedia.org incorrect or just misunderstood something or is gcc incompatible in this case (by the way, Visual Studio 2015 gives the same unexpected result)?

EDIT:

I understand that the order of your_function arguments is evaluated and assigned to undefined parameters. I am not asking why my average term is 0. I am asking why the other two conditions are 12.

+11
c ++ c sequence-points printf format-specifiers
Jan 06 '16 at 16:16
source share
3 answers

Since this question has been asked due to discussion based on comments here , I will provide some context:

first comment: the order of operations is not guaranteed to be the order in which you pass arguments to a function. Some people (by mistake) assume that the arguments will be evaluated from right to left, but according to the standard, the behavior is undefined.

The OP accepts and understands this. It makes no sense to repeat the fact that your_function(++i, ++i) is UB.

In response to this comment: thanks to your comment, I see that printf can be evaluated in any order, but I realized that this is because the printf arguments are part of va_list . Are you saying that the arguments of any function are executed in random order?

OP asks for clarification, so I clarified a bit:

Second comment: Yes, that’s exactly what I’m saying. even calling int your_function(int a, int b) { return a - b; } int your_function(int a, int b) { return a - b; } does not guarantee that the expressions you pass will be evaluated from left to right. There is no point in the sequence (the point at which all side effects of previous evaluations are performed). Take this example . The nested call is the sequence point, so the external call passes i+1 (13), and the return value of the internal call (undefined, in this case -1, because i++ , i evaluates to 12, 13, obviously) , but there is no guarantee, that this will always be the case

This made it pretty clear that these types of constructs launch UB for all functions.




Wikipedia Concern

OP Quotes this:

After the action associated with the input / output conversion format specifier. For example, in the expression printf ("foo% n% d", & a, 42), there is a sequence point after evaluating% n before printing 42.

Then it applies it to his fragment ( prinf("%d - %d - %d\n", i, your_function(++i, ++i), i); ), chasing format specifiers to serve as sequence points.
What the "I / O format specifier" refers to is the %n specifier. The corresponding argument must be a pointer to an unsigned integer, and it will be assigned the number of characters printed so far. Naturally, %n must be evaluated before the rest of the arguments are printed. However, using the pointer passed to %n in other arguments is still dangerous: it is not UB (well, it is not, but it may be):

 printf("Foo %n %*s\n", &a, 100-a, "Bar");//DANGER!! 

There is a sequence point before the function call, so the expression 100-a will be evaluated before %n sets &a to the correct value. If a initialized, then 100-a is UB. If a initialized to 0, for example, the result of the expression will be 100. In general, however, this kind of code requires a lot of trouble. See it as a very bad practice, or worse ...
Just look at the result generated by one of the following statements:

 unsigned int a = 90; printf("%u %n %*s\n",a, &a, 10, "Bar");//90 Bar printf("%u\n", a);//3 printf("Foo %u %n %*s\n",a, &a, 10-a, "Bar");//Foo 3 Bar < padding used: 10 - 3, not 10 - 6 printf("%u\n", a);//6 

As you can see, n reassigned inside printf , so you cannot use its new value in the argument list (because there is a sequence point). If you expect n be reassigned β€œin place,” you essentially expect C to jump out of the function call, evaluate the other arguments, and return back to the call. It is simply not possible. If you were to change unsigned int a = 90; on unsigned int a; , then the behavior is undefined.




As for the 12 '

Now, when the OP reads the points of the sequence, it correctly notices that this statement:

 printf("%d - %d - %d\n", i, your_function(++i, ++i), i); 

A little different: your_function(++i, ++i) is a point in the sequence, but ensures that i will double up. This function call is a sequence point because:

Before a function is introduced into a function call. The order in which the arguments are evaluated is not specified, but this point in the sequence means that all of their side effects are complete before the function is entered

This means that before calling printf your_function must be called (since its return value is one of the arguments for calling printf ) and i will double. This one could explain that the result is "12 - 0 - 12", but is this a guaranteed way out?

No

Technically, although most compilers will first appreciate the call to your_function(++i, ++i); , the standard will allow the compiler to evaluate the arguments passed to sprintf from left to right (order is not specified after all). Thus, this will be an equal result:

 10 - 0 - 12 //or even 12 - 0 - 10 //and 10 - 0 - 10 //technically, even this would be valid 12 - 0 - 11 

Although the last exit is extremely unlikely (it would be very inefficient)

+6
Jan 06 '16 at 16:58
source share

I think you misunderstood the text about the points in the printf (SP) sequence. They are somehow anomalies and only with %n , because this format specifier has side effects, and these side effects need to be sorted.

Anyway, there is SP at the beginning of printf() and after evaluating all the arguments. These SP format specifiers are all after that, so they do not affect your problem.

In your example, using i all in the arguments of the function, and none of them are separated by points in the sequence. Since you change the value (twice) and use the value without intermediate points in the sequence, your code is UB.

What is the rule about SP in printf means that this code is well-formed:

 int x; printf("%d %n %d %n\n", 1, &x, 2, &x); 

although the value of x changes twice.

But this code is UB:

 int x = 1; printf("%d %d\n", x, ++x); 

NOTE. Remember that %n means that the number of characters written so far is copied to the integer specified by the specified argument.

+11
Jan 06 '16 at 16:23
source share

Having accepted a clear answer to this question, it is strictly fulfilled (even prevented) by rules C in the order of evaluation and UB.

The indicated rules for the evaluation procedure are indicated here:

C99 section 6.7.9, p23: 23 The ratings of the expression initialization list are vaguely ordered relative to each other and, therefore, the order in which any side effects occur is unspecified.

And this function call will demonstrate undefined behavior :

 your_function(++i, ++i) 

Due to UB, combined with the rules of the evaluation procedure, accurate predictions of expected results for the following:

 printf("%d - %d - %d\n", i, your_function(++i, ++i), i); 

impossible.

Edit
... I do not ask why my average term is 0. I ask why the other two conditions are equal.

There is no guarantee which of the three arguments to the above function is called first. (due to rules C in the evaluation order). And if the first function is evaluated first, then at that moment you called Undefined Behavior . Who can really say why the other two conditions are 12 ?. Because what happens to i when the second argument is evaluated, someone guesses.

+4
Jan 6 '16 at 16:25
source share



All Articles