a comma , defines a sequence
No. Don't confuse the comma operator (What does the comma operator , do?) for a function argument list. The actual ,
symbol is used in a lot of different places in the C syntax, but the only place where it has a well-defined order of evaluation is when it's used for the actual comma operator. Neither function call argument lists nor initializer lists have well-defined orders.
The C standard says this about function calls and arguments (C17 6.5.2.2):
There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.
I your case the 4 arguments "%d %d %d"
, i
, ++i
, i++
are indeterminately sequenced in relation to each other and the sequence point, as mentioned above, is placed after the evaluation of the arguments.
To address your other statements:
-
the actual printing happens when all of the sequences are evaluated inside the function argument call
Correct, but then undefined behavior has already struck.
-
since arguments are pass-by-value, a copy happens sometime(?!) while calling the function
You can assume that these are pass by value indeed. printf
is an icky variadic function so they end up in a va_list
, which I believe has an implementation-defined representation internally.
-
the order in which the function argument sequences evaluated is undefined ( is this true? )
The order of evaluation of function arguments is unspecified behavior, meaning we can't know the order - it can be undocumented - and therefore we shouldn't assume a particular order. The actual undefined behavior happens because of 6.5/2:
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.
-
I would like to understand the parts that are NOT undefined behavior.
I'm not sure what you mean with that, since code can't be "just a little bit undefined". If it has undefined behavior and then all bets are off regarding anything in that program. You can't reason about it or it wouldn't be undefined, but something else. Therefore it doesn't make sense to reason like "undefined behavior turns this value to 55 and then it is passed by value". For example maybe the UB causes the whole function call to be optimized away or get inlined in strange ways.