在下面的代码中摘录了一段较大的代码
void func(int* usedNum, int wher) {
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
}
int main(void) {
int a = 11, b = 2;
func(&a, b);
}
Run Code Online (Sandbox Code Playgroud)
一个警告发出
warning: operation on '* usedNum' may be undefined [-Wsequence-point]
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
Run Code Online (Sandbox Code Playgroud)
代码有问题吗?
我怀疑的是这个以及它所说的部分
序列指向逻辑表达式,例如&&和|| 和三元运算符?:和逗号运算符表示在右侧操作数之前计算左侧操作数.这几个操作数是C++中唯一引入序列点的操作数.
对于那些发现折磨阅读评论的人:最初的问题没有恰当地提出,造成误解是不公平的.我对这个主题的看法有两个方面
三元运算符不会(以意想不到的方式)混乱序列点(其中,两个分支在C,C++的每个版本中排序 - 请参阅提供的链接)
是x = ++x
问题吗?如coliru链接所示,我们编译为c ++ 14.那里的操作定义很好(对注释的引用),但旧版本的c ++和c将其视为未定义.那为什么会有警告?
答案集中在C和C++中; 这是一个很好的链接.最后,C标签最初是(我的坏)并且无法删除,因为现有的upvoted答案引用它
jua*_*nza 17
当条件为真时,它相当于说x = ++x
.在C和C++ 11之前的C++版本中,这构成了一个修改和读取而x
没有插入序列点,因此如果遵循truthy分支则是未定义的行为.从C++ 11开始,对其x = ++x
进行排序和定义.
编辑从注释中澄清一些问题.
1)这将在所有C和C++标准中明确定义:
x = (++x, x); // RHS evaluates to x after increment
Run Code Online (Sandbox Code Playgroud)
因为括号中的表达式涉及逗号运算符,它在其操作数的求值之间引入了一个序列点.因此,RHS上的整个表达式在增量x
后评估.但是你问题中的代码不涉及逗号运算符.
2)三元运算符引入序列点
它是条件和两个分支之间的序列点.但是这不会在分支和赋值之间引入序列点.
您收到的警告可能是因为您正在以c ++ 03模式或更早版本编译代码.在C99和C++ 03中,表达式
x = ++x;
Run Code Online (Sandbox Code Playgroud)
调用未定义的行为.原因是在两个序列点之间,对象不能多次修改.
此规则在C11和C++ 11中已更改.根据C11,规则如下:
如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义.
什么时候*usedNum + 1 > wher
会true
,那么
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
Run Code Online (Sandbox Code Playgroud)
相当于
*usedNum = ++(*usedNum);
Run Code Online (Sandbox Code Playgroud)
并且根据新规则,这在C++ 11中得到了很好的定义,这是因为pre ++
的副作用在=
操作员的副作用之前被排序.阅读此答案以获得更详细的解释.
但是同一个表达式在C11中*usedNum = ++(*usedNum);
调用了未定义的行为.原因是在=
操作员的副作用之后,无法保证操作员的副作用是有序的++
.
注意:在表达式中
a = x++ ? x++ : 0;
Run Code Online (Sandbox Code Playgroud)
在第一个之后存在序列点x++
,因此行为被很好地定义.同样如此
x = (++x, x);
Run Code Online (Sandbox Code Playgroud)
因为在左右操作数的评估之间存在序列点,因此对副作用进行了排序.