【问题标题】:Is this operation properly sequenced?此操作的顺序是否正确?
【发布时间】:2015-02-10 09:05:29
【问题描述】:

在以下代码中摘自一段较大的代码

void func(int* usedNum, int wher) {
    *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 
}

int main(void) {
   int a = 11, b = 2; 
   func(&a, b); 
}

警告是emitted

 warning: operation on '* usedNum' may be undefined [-Wsequence-point]
 *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 

代码有问题吗?

我的怀疑来源是this 以及它所说的部分

&&、||等逻辑表达式中的序列点三元运算符 ?: 和逗号运算符表示左侧操作数在右侧操作数之前计算。这几个操作数是 C++ 中唯一引入序列点的操作数。

tl;博士

对于那些在阅读 cmets 时感到折磨的人:最初的问题没有正确提出,造成误解是不公平的。我对这个话题的看法有两个方面

  1. 三元运算符不会(以意想不到的方式)弄乱序列点(这两个分支在每个版本的 C、C++ 中都是有序的 - 请参见提供的链接)

    李>
  2. x = ++x 有问题吗?如 coliru 链接所示,我们为 c++14 编译。那里的操作定义良好(对 cme​​ts 的引用),但旧版本的 c++ 和 c 将其视为未定义。那么为什么会有警告呢?

答案集中在 C 和 C++; this 是一个很好的链接。最后,C 标记最初存在(我的错误)并且无法删除,因为现有的赞成答案引用它

【问题讨论】:

  • 不就是说x = ++x吗?所以是的,如果遵循truthy分支,潜在的UB。
  • @juanchopanza 不等于说x = (++x) 吗?根据this,哪个定义明确? (看看上面写着i = (++i,i++,i) // well defined的部分)
  • 逗号运算符引入序列点。您问题中的代码没有逗号运算符。
  • 这看起来像 C++,为什么是 C 标签?
  • 是的,这意味着abc 之前在a ? b : c 中被评估,但这并不意味着整个事情在分配的LHS 之前被排序。跨度>

标签: c++ c


【解决方案1】:

当条件为真时,相当于说x = ++x。在 C 和 C++11 之前的 C++ 版本中,这构成了对 x 的修改和读取没有中间序列点,因此是未定义的行为如果遵循truthy分支。从 C++11 开始,x = ++x is sequenced and well defined


编辑以澄清 cmets 的一些问题。

1) this 在所有 C 和 C++ 标准中都有很好的定义:

x = (++x, x); // RHS evaluates to x after increment

因为括号中的表达式涉及逗号运算符,它在其操作数的求值之间引入了一个序列点。因此,RHS 上的整个表达式计算为 x 一个增量之后。但是您问题中的代码不涉及逗号运算符。

2) 三元运算符引入一个序列点

它是条件和两个分支之间的序列点。但这并没有在任一分支和赋值之间引入序列点。

【讨论】:

  • 不等于说x = (++x) 吗?根据this,哪个定义明确?
  • 我很确定 x = ++x; 在 C++11 中定义良好。
  • @T.C.我阅读了问题的相关部分和 C 标签。这两个答案都是 C. OP 正确标记事物的问题。
  • @remyabel +1 在那里很重要;在 C++11 中 i = ++i; 是 UB,但 i = ++i + 1; 可以
  • @T.C.; x = ++x; 在 C11 中也有很好的定义。
【解决方案2】:

您收到的警告可能是由于您在 c++03 或更早的模式下编译代码。在 C99 和 C++03 中,表达式

x = ++x;

调用未定义的行为。原因是在两个序列点之间,一个对象不能多次修改。

此规则在 C11 和 C++11 中有所更改。根据C11,规则如下:

C11:6.5 表达式:

如果标量对象的副作用未排序相对于同一标量对象的不同副作用或使用相同标量对象的值的值计算,则行为未定义。

*usedNum + 1 > wher 将是true,那么

*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;   

相当于

*usedNum = ++(*usedNum);  

根据新规则,这在 C++11 中得到了很好的定义,这是因为 pre ++ 的副作用在 = 运算符的副作用之前排序。阅读this answer了解更详细的解释。

但同样的表达式 *usedNum = ++(*usedNum); 在 C11 中调用 未定义的行为。原因是不能保证=操作符的副作用排在前++操作符的副作用之后。


注意:在表达式中

a = x++ ? x++ : 0; 

在第一个x++ 之后有一个序列点,因此行为是明确定义的。

x = (++x, x);  

因为左右操作数的求值之间有一个序列点,因此副作用是有序的。

【讨论】:

  • @NikosAthanasiou 这些括号没有区别。
  • @NikosAthanasiou;更新了答案。
  • 我不确定我是否在 C 标准中看到了关于表达式 x += 1 的值计算与副作用之间的相对顺序的任何内容。没有这个,表达式执行的两个赋值之间就没有确定的顺序。
  • @T.C.; x += 1 在分配给x 之前在x = (x += 1) 中排序。
  • @hacks 不,x += 1值计算 在分配到 x = (x += 1) 中的 x 之前排序。这并没有说明x += 1副作用
猜你喜欢
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多