行为是未指定的,而不是未定义的。
C 标准区分了这些。C 2018 3.4.4 1 说:
未指明的行为
行为,是由于使用未指定的值,或本文档提供两种或多种可能性的其他行为,并且在任何情况下都没有对其选择的进一步要求
3.4.3 1 说:
未定义的行为
行为,在使用不可移植或错误程序构造或错误数据时,本文档对此没有任何要求
在某些情况下,当一个对象既用于其值又被修改时,C 标准中的一条规则使行为未定义。6.5 2 说:
如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。如果一个表达式的子表达式有多个允许的排序,则如果在任何排序中出现这种未排序的副作用,则行为是未定义的。
让我们看看这如何适用于a=a+puan_ekle(a,b);
. 在这个表达式中:
a
由 修改a=
。
a
用于a+
.
a
在参数中使用(a,b)
。
- 函数里面
puan_ekle
,a
是用 修改的a=puan-5;
。
修改是副作用——它们与计算表达式的值是分开发生的。如果修改 1 或 4 中的任何一个相对于任何其他项未排序,则行为未定义。
关于 1,6.5.16 3 说:
…更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的……</p>
所以 1 在 2 和 3 之后排序。由于 4 是副作用,而不是值计算,我们仍然必须考虑 1 和 4 的关系。为了解决这个问题,我们将考虑序列点。根据 5.1.2.3,“在表达式A和B的求值之间存在序列点意味着与A关联的每个值计算和副作用都在与B关联的每个值计算和副作用之前排序。”</p>
接下来我们需要知道什么是完整表达式,以及每个完整表达式后面都有一个序列点。6.8 4 说:
完整表达式是不属于另一个表达式的表达式,也不是声明符或抽象声明符的一部分……在完整表达式的评估和要评估的下一个完整表达式的评估之间存在一个序列点。
这意味着里面的每个语句puan_ekle
都是或包含一个完整的表达式:puan=puan+bonus
是一个完整的表达式,a=puan-5
是一个完整的表达式,bonus--
是一个完整的表达式,而bonus
inreturn bonus
是一个完整的表达式。所以,在 之后a=puan-5
,有一个序列点。
因为,对于a=
,修改的副作用a
是在操作数的值计算之后排序的。评估这些操作数包括调用函数,其中包括其序列点。所以 effect 4 修改a
ina=puan-5;
必须在执行到下一条语句之前完成,因此必须在 effect 1 之前完成。所以 1 和 4 是有序的。
剩下的就是考虑关于 2 和 3 的效果 4。根据 6.5.2.2 10,函数调用在评估其参数之后排序,因为函数调用在 3 之后排序:
在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点……</p>
现在我们剩下的就是 2 相对于 4 的排序。在此,没有指定哪个是第一个。的操作数的评估+
是无序的,因此,对于a+puan_ekle(a,b)
,C 实现可以a
先执行,也可以先执行puan_ekle(a,b)
。但是,无论它先做什么,在 2 和 4 之间都有一个序列点:
- 如果
a
首先评估,则在函数调用之前,有一个序列点(根据 6.5.2.2 10,上面引用)。
- 如果
puan_ekle(a,b)
首先评估,则在完整表达式之后有一个序列点a=puan-5
。
因此,2 和 4 是不确定排序的。(5.1.2.3 3:“……当 A 在 B 之前或之后排序时,评估A和B的排序不确定,但未指定哪个……”)但它们不是未排序的,因此没有未定义的行为。该行为未指定,因为有两种可能性。C 实现需要实现这两种可能性中的一种,这与未定义的行为不同,其中没有要求。