函数调用何时复制其相对于参数序列的按值传递参数?

When does a function call copy its pass-by-value arguments relative to the argument sequences?

我想在未定义行为允许的范围内尽可能多地理解这段代码:

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

输出:

2 2 0

据我所知:

据我所知,那一行代码中的大部分行为是未定义的,但我仍然想了解不是未定义行为的部分。 我知道输出取决于编译器,但 C 标准中定义了哪些部分? 我对 ANSI C、C99 也很感兴趣,但我相信最新的 C++ 标准至少在某些方面对此有所改进,是这样吗?

a comma , defines a sequence

没有。不要将逗号运算符 (What does the comma operator , do?) 与函数参数列表混淆。实际的 , 符号在 C 语法中的许多不同地方使用,但唯一具有明确定义的评估顺序的地方是它用于实际的逗号运算符。函数调用参数列表和初始化列表都没有明确定义的顺序。

C 标准关于函数调用和参数的说明 (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.

在你的情况下,4 个参数 "%d %d %d"i++ii++ 相对于彼此和序列点不确定地排序,如上所述, 被放置在 之后 参数的评估。


解决您的其他陈述:

  • the actual printing happens when all of the sequences are evaluated inside the function argument call

    正确,但未定义的行为已经出现。

  • since arguments are pass-by-value, a copy happens sometime(?!) while calling the function

    你可以假设这些确实是按值传递的。 printf 是一个棘手的可变参数函数,所以它们以 va_list 结束,我相信它在内部有一个实现定义的表示。

  • the order in which the function argument sequences evaluated is undefined ( is this true? )

    函数参数的求值顺序是未指定的行为,这意味着我们无法知道顺序 - 它可能没有记录 - 因此我们不应该假设一个特定的顺序.实际未定义的行为发生是因为 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.

    我不确定你的意思,因为代码不能“只是有点未定义”。如果它有未定义的行为,那么所有关于该程序中 anything 的赌注都会被取消。你不能对它进行推理,或者它不会是未定义的,而是别的东西。因此,像“未定义的行为将这个值变成 55 然后按值传递”这样的推理是没有意义的。例如,UB 可能导致整个函数调用被优化掉或以奇怪的方式内联。