在下面的代码中摘录了一大段代码
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 ++中唯一引入序列点的操作数。
对于那些发现折磨阅读评论的人:最初的问题没有恰当地提出,造成误解是不公平的。我对这个主题的看法有两面性
三元运算符不会(以意想不到的方式)弄乱序列点(保持,两个分支在每个版本的C,C ++中排序 - 请参阅提供的链接)
问题是x = ++x
吗?如coliru链接所示,我们编译为c ++ 14。那里的操作定义很好(对注释的引用),但旧版本的c ++和c将其视为未定义。那为什么会有警告?
答案集中在C和C ++; this是一个很好的链接。最后,C标签最初是(我的坏)并且无法删除,因为现有的upvoted答案引用它
答案 0 :(得分:17)
当条件为真时,它相当于说x = ++x
。在C和C ++ 11之前的C ++版本中,这构成了x
的修改和读取,没有插入序列点因此是未定义的行为如果遵循真正的分支。从C ++ 11开始,x = ++x
is sequenced and well defined。
修改澄清评论中的一些问题。
1)这个将在所有C和C ++标准中得到很好的定义:
x = (++x, x); // RHS evaluates to x after increment
因为括号中的表达式涉及逗号运算符,它在其操作数的求值之间引入了一个序列点。因此,在增量之后,RHS上的整个表达式将评估为x
。但是你问题中的代码不涉及逗号运算符。
2)三元运算符引入序列点
它是条件和两个分支之间的序列点。但这并没有在分支和赋值之间引入一个序列点。
答案 1 :(得分:7)
您获得的警告可能是由于您正在以c ++ 03模式或更早版本编译代码。在C99和C ++ 03中,表达式
x = ++x;
调用未定义的行为。原因是在两个序列点之间,对象不能多次修改。
此规则在C11和C ++ 11中已更改。根据C11,规则如下:
如果相对于同一标量对象的不同副作用或使用相同标量对象的值进行值计算,标量对象的副作用未序列,则行为未定义。
当*usedNum + 1 > wher
为true
时,
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
等同于
*usedNum = ++(*usedNum);
根据新规则,这在C ++ 11中有明确定义,这是因为前++
的副作用在=
运算符的副作用之前排序。请阅读this answer以获取更详细的说明。
但是同一个表达式*usedNum = ++(*usedNum);
在C11中调用未定义的行为。原因是,无法保证=
运算符的副作用在前++
运算符的副作用后排序。
注意:在表达式
中a = x++ ? x++ : 0;
在第一个x++
之后有序列点,因此行为定义明确。
x = (++x, x);
因为在左右操作数的评估之间存在一个序列点,因此对副作用进行了排序。