这是未定义的行为吗?
arr[i++] += x; // [1]
// arr is an array of integer type and x is an integer type variable
// Assumptions -
// 1. Value of i is a valid index value of array arr and
// the result of prefix/postfix increment/decrement of i
// is also a valid index value of array arr.
// 2. The value of array members and variable x is such that
// arithmetic operation (+) does not result in overflow.
gcc
并且clang
编译器不会在语句上给出任何警告消息[1]
,但如果我[1]
用此语句替换 -
arr[i++] = arr[i++] + x; // [2]
他们对这个声明发出了一个警告信息,这是非常明显的——
gcc
编译器:
warning: operation on ‘i’ may be undefined [-Wsequence-point]
arr[i++] = arr[i++] + x;
clang
编译器:
warning: multiple unsequenced modifications to 'i' [-Wunsequenced]
arr[i++] = arr[i++] + x;
乍一看,该语句似乎表现出未定义的行为,但事实并非如此。
来自 c17#6.5.16.2 $4 [已添加强调]
另请检查下面的参考文献1)。
在此语句中,左值
arr[i++]
将仅计算一次它将被视为
类似地,如果使用前缀增量,则语句
将被视为
因此,对 的值的修改
i
并不是无序地对 的值进行不同的修改i
,或者使用 的值i
进行计算arr[i]
。[参考1) ]因此,该语句
arr[i++] += x;
不会表现出未定义的行为。[如果在语句中使用前缀/后缀递减,则同样保留
arr[i++] += x;
。]鉴于,在声明中
[2]
——LHS 和 RHS 的评估
i++
是无序的:它们可以按任何顺序执行(在单个执行线程内,编译器可以交错包含 LHS 和 RHSi++
评估的 CPU 指令)。如果标量对象上的副作用相对于同一标量对象上的另一个副作用是无序的,则该行为是未定义的,因此编译器会发出警告。请注意,以下语句也表现出未定义的行为
出于与上述相同的原因。
1)。参考:
c17#6.5 $2
c17#6.5.16 $3
是明确定义的;序列点之间不会多次修改对象。它实际上相当于
需要注意的是,最后两次更新可以按任何顺序发生,甚至可以同时发生。
相比之下,
导致未定义的行为,因为
i
在序列点之间修改了多次。甚至和
导致未定义的行为,因为
i
两者都被更新并用于序列点之间的值计算。算术表达式不保证从左到右计算,也不保证副作用在计算后立即应用;上述表达式可以产生多个结果,并且就语言定义而言,所有这些不同的结果都同样正确。
要呈现这些明确定义的内容,请将 的更新
i
与 的更新分开arr[i]
:或者,只需使用