There is such code:

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

Conclusion: 7 6 5
Why is this conclusion happening? Said related to a variable number of arguments.

  • because it is ub clean. There is no following point. But the parameters are usually “from the end” calculated to be more conveniently stack - pavel
  • @pavel On i386, arguments are on the stack, but on AMD64 they can be transferred through registers, which leads to tambourine dances when implementing va_arg . - jfs
  • @jfs: Since when has the method of passing parameters to a function become dependent on the hardware platform? - AnT
  • @AnT you probably want to convey some idea (with the word "compiler", judging by your story) Make a positive statement right away. - jfs

2 answers 2

A very simple explanation is that since the standard says nothing about the order in which the function arguments are calculated, the compiler does this as it sees fit. So you get 7 6 5 , you can get 5 6 7 or, say, 6 5 7 ... You get what is called uncertain behavior .

The explanation is more precise, but also more complicated - read the literature about the points of the sequence ... For example, here or here .

  • 1- if you believe the link: f(++k, ++k) is "undefined behavior" in C, and "unspecified behavior" after c ++ 17 . 2- After the actions related to% n in printf, there is a "sequence point" in c99 . True c ++k does not help. - jfs
  • @jfs: Fundamental changes in C ++ started back in C ++ 11 (where i= ++i became definite), but if we consider that they were caused by defectiveness of the entire model sequencing in C ++ 98, then we can say that C and C ++ went in diametrically opposite ways from the very beginning. Therefore, what happens in this area in C ++ has nothing to do with C. The semantics ++i in C ++ has practically nothing to do with the semantics ++i in C. - AnT
  • The answer is misleading. (And in the context of the presence of the [language-lawyer] tag in the question, the answer is grossly wrong). The ability to get 7 6 5 il 5 6 7 would be unspecified behavior. In this case, we have an indefinite behavior, which is a fundamentally different phenomenon. - AnT

Firstly, the question is not related to the variable number of parameters for the printf function at all.

Secondly, the behavior in your example is undefined (undefined behavior), because the multiple side effects affecting the variable i (increasing the value of i by one) are in no way ordered relative to each other.

Please note that this phenomenon cannot be explained by the uncertainty of the order in which the function arguments are calculated . Similar explanations are a popular and quite common misconception. In fact, it is the uncertainty of order, as such, has nothing to do with the emergence of uncertain behavior in this case. If to be expressed "on fingers", then it is more important that the calculations of independent expression-arguments are not isolated from each other and can be "intertwined" with each other as you like.

And, for example, in this code

 int foo(int *p) { return (*p)++; } int i = 5; printf("%d %d %d", foo(&i), foo(&i), foo(&i)); 

The order of calculating the arguments is also not specified, but (!) there is no undefined behavior here. Behavior is simply not specified. Informally, the reason for this is precisely that the execution of function bodies in two independent calls cannot be "intertwined" - they are ordered relative to each other, even if in this case it is unknown how.

Pay attention to this subtlety: in your example, the side effects are not ordered at all , but in my example they are ordered in an unspecified way . These are different things. In your case, the behavior is not defined, in my example - the behavior is not specified.

It is in my example that you can get 5 6 7 , and 7 6 5 , etc. Behavior is not specified, but limited to a clearly defined set of options. In your own code, absolutely anything can happen, up to the compiler's failure to compile your code or to the textbook “formatting a hard disk” and launching even more textbook “nasal demons”.

  • At least gcc -O3 on Linux X86_64 takes a different view, as seen in movl $5, %r8d; movl $6, %ecx; movl $7, %edx movl $5, %r8d; movl $6, %ecx; movl $7, %edx movl $5, %r8d; movl $6, %ecx; movl $7, %edx before calling printf (especially considering that i = 5 simply not in the generated code) - avp
  • @avp I do not understand what exactly you saw in the generated gcc code. What “other” point of view are you talking about? "Other" in relation to what? And what could be the “points of view” at all in a situation where the behavior is not defined or not specified? Or do you just want to notice that the gcc decided not to format the hard disk? - AnT
  • Well, with some degree of stretching, the program’s interpretation (calculation of its result) in the compilation process can be called behavior (of course, rather, it is the behavior of the program authors encoded in it). And it does not matter at all whether the standard considers such a situation "not defined or not specified". / In this case, it is quite obvious that the list of printf arguments is calculated from right to left, taking into account the change in the value of the variable (purely logical, since there is simply no physical memory in the resulting code) - avp
  • @avp: First, if you try to draw such reaching conclusions about the behavior of gcc based on such a trivial example, you only get the bullshit. The GCC compiler in such contexts does not provide any certainty, not even the level of a specific implementation (not to mention the fact that even if there was such certainty, it would still not concern the question at all). Your "it is absolutely obvious that the list of printf arguments is calculated from right to left" - this is nothing more than naive conjectures from the field of "footless cockroach does not hear" from Vasily Ivanovich's dissertation in a well-known joke. - AnT
  • Secondly, your attempts to pull the "order of calculating arguments" here behind the ears cause only bewilderment. The order of calculating the arguments here is irrelevant and does not play any role to the question - I authoritatively and finally closed this topic in my answer. I will not repeat. If you do not understand something in the answer - do not hesitate to ask. It is not necessary instead of direct questions to create a noise background in the comments through some random, meaningless examples. - AnT