Jay*_*esh 18 c c++ undefined-behavior sequence-points c++14
在C和C++语言中,该arr[i] = i++;语句调用未定义的行为.为什么语句i = i + 1;不会调用未定义的行为?
Sou*_*osh 36
由于这最初是用c和c ++标记的,而不是任何特定的版本,因此下面的答案是问题的通用答案.但请注意,对于c ++,C++17从那时起,行为就发生了变化.请参阅Barry的回答以了解更多信息.
对于声明
arr[i] = i++;
Run Code Online (Sandbox Code Playgroud)
的值i被用于在两个操作数,RHS(右手侧)和LHS(左手侧),并且在其中一种情况下,该值被修改(作为后的副作用++),其中有两者之间没有序列点来确定i应该考虑哪个值.您还可以查看此规范答案以获取更多信息.
在另一方面,对i = i + 1,价值i是使用只在RHS,计算结果存储在LHS,换句话说,没有歧义.我们可以写相同的语句i++,这
i1i在明确定义的序列中.因此,没有问题.
Bar*_*rry 12
请注意,这将在C++ 17中发生变化.在C++ 17中,arr[i] = i++不会调用未定义的行为.这是由于[expr.ass]中的以下更改:
在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.右操作数在左操作数之前排序.
也就是说,我们不i++ 那么我们就arr[i] 接着我们进行分配.现在明确定义的顺序是:
auto src = i++;
auto& dst = arr[i];
dst = src;
Run Code Online (Sandbox Code Playgroud)
对于C99,我们有:
6.5表达式
- 在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值.
在arr[i] = i++,值i只修改一次.但arr[i]也读取i,并且此值不用于确定新值i.这就是为什么它有未定义的行为.
另一方面,在i = i + 1我们读取i以便计算时i + 1,它被用作新的值i.因此这个表达很好.
arr[i] = i++;
Run Code Online (Sandbox Code Playgroud)
暗示
但是包含右手表达式评估和下标操作员评估顺序的含糊不清,编译器可以自由地将其视为
auto & val{arr[i]};
i++;
auto const rval{i};
val = rval;
Run Code Online (Sandbox Code Playgroud)
或者作为
i++;
auto & val{arr[i]};
auto const rval{i};
val = rval;
Run Code Online (Sandbox Code Playgroud)
或者(与上述相同的结果)
i++;
auto const rval{i};
auto & val{arr[i]};
val = rval;
Run Code Online (Sandbox Code Playgroud)
这可能会产生不可预测的结果
i = i + 1;
Run Code Online (Sandbox Code Playgroud)
dos没有任何歧义,在赋值前评估右手表达式:
auto const rval{i + 1};
auto & val{i};
val = rval;
Run Code Online (Sandbox Code Playgroud)
或(与上述相同)
auto & val{i};
auto const rval{i + 1};
val = rval;
Run Code Online (Sandbox Code Playgroud)