我想尽可能多地理解这个代码片段,因为未定义的行为允许它:
int i = 0;
printf("%d %d %d", i, ++i, i++);
输出:
2 2 0
据我所知:
,
定义了一个序列因此,据我所知,该单行代码中的大部分行为是未定义的,但我仍然想了解不是未定义行为的部分。我知道输出取决于编译器,但是 C 标准中定义了哪些部分?我也对 ANSI C、C99 感兴趣,但我相信最新的 C++ 标准至少在某些方面对此有所改进,这是真的吗?
逗号,定义一个序列
不。不要混淆函数参数列表中的逗号操作符(逗号操作符的作用是什么?)。实际,
符号在 C 语法中的许多不同地方使用,但它具有明确定义的求值顺序的唯一地方是当它用于实际的逗号运算符时。函数调用参数列表和初始值设定项列表都没有明确定义的顺序。
C 标准是关于函数调用和参数的(C17 6.5.2.2):
在函数指示符和实际参数的计算之后但在实际调用之前有一个序列点。调用函数(包括其他函数调用)中的每一个在被调用函数体执行之前或之后没有特别排序的评估都相对于被调用函数的执行是不确定的。
在你的情况下,4 个参数"%d %d %d"
, i
, ++i
,i++
相互之间是不确定的,序列点,如上所述,放在参数评估之后。
要解决您的其他陈述:
当在函数参数调用中计算所有序列时,会发生实际打印
正确,但未定义的行为已经发生。
由于参数是按值传递的,因此在调用函数时有时会发生副本(?!)
您可以假设这些确实是按值传递的。printf
是一个 icky 可变参数函数,所以它们最终在 a 中va_list
,我相信它在内部有一个实现定义的表示。
函数参数序列求值的顺序未定义(这是真的吗?)
函数参数的求值顺序是未指定的行为,这意味着我们无法知道顺序 - 它可能是未记录的 - 因此我们不应该假设特定的顺序。由于 6.5/2,实际的未定义行为发生了:
如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用同一标量对象的值进行的值计算而言是未排序的,则行为未定义。如果一个表达式的子表达式有多个允许的排序,并且在任何排序中出现这种未排序的副作用,则行为是未定义的。
我想了解不是未定义行为的部分。
我不确定你的意思,因为代码不能“只是有点未定义”。如果它有未定义的行为,那么关于该程序中的任何内容的所有赌注都将关闭。你不能对它进行推理,否则它不会是未定义的,而是别的东西。因此,像“未定义的行为将此值转换为 55 然后按值传递”这样的推理是没有意义的。例如,UB 可能会导致整个函数调用被优化掉或以奇怪的方式内联。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句