这是一个例子
#include <iostream>
using namespace std;
int main()
{
int x = 0;
cout << (x == 0 ? x++ : x) << endl; //operator in branch
cout << "x=" << x << endl;
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
cout << "x=" << x << endl;
return 0;
}
输出:
0
x=1
1
x=1
我理解输出,但是否是未定义的行为?两种情况下的评估顺序是否得到保证?
即使有保证,我也非常清楚使用递增/递减会很快成为可读性的问题。我只是问,因为我看到类似的代码,并且立即不确定,因为有很多关于递增/递减运算符的模糊/未定义使用的示例,例如......
答案 0 :(得分:34)
对于条件运算符(§5.16[expr.cond] / p1):
与第一个相关的每个值计算和副作用 在每个值计算和副作用之前对表达式进行排序 与第二或第三个表达相关联。
对于逻辑OR运算符(§5.15[expr.log.or] / p1-2):
如果第一个操作数的计算结果为
true
,则不计算第二个操作数。 [...]如果计算第二个表达式,则每个值计算和 与第一个表达相关的副作用在之前排序 与第二个相关的每个值计算和副作用 表达
您的代码行为定义明确。
答案 1 :(得分:10)
三元运算符和布尔&&
和||
运算中保证执行顺序,因此评估序列点没有冲突。
一次一个
cout << (x == 0 ? x++ : x) << endl; //operator in branch
将始终输出x
但仅在它为0时才会递增。
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
这个定义也很明确,如果x
为1,它将不会评估RHS,如果不是,它会递减它,但--x
永远不会为0,所以iff将是真的x == 1,在这种情况下x
现在也将为0。
在后一种情况下,如果x是INT_MIN
,那么减去它就没有明确定义的行为(它会执行)。
在第一种情况下,如果INT_MAX
x不会为0,则不会发生这种情况。所以你是安全的。
答案 2 :(得分:7)
我理解输出,但这是不确定的行为?
代码完美定义。 C11标准说:
评估第一个操作数; 评估与评估之间存在一个序列点 评估第二或第三操作数(以评估者为准)。第二个操作数 仅当第一个比较不等于
0
时才进行评估;第三个操作数仅在以下情况下进行评估 第一个比较等于0
;结果是第二个或第三个操作数的值 (以评估者为准),转换为下述类型.110)
与按位
|
运算符不同,||
运算符保证从左到右的评估;如果 评估第二个操作数,第一个操作数的评估之间存在一个序列点 和第二个操作数。如果第一个操作数与0
不相等,则第二个操作数为 没有评估。
进一步wiki以示例解释:
评估
&&
(逻辑AND)的左右操作数,||
(逻辑OR)(作为短路评估的一部分)和{{1} }。例如,在表达式comma operators
中,在尝试访问*p++ != 0 && *q++ != 0
之前,子表达式*p++ != 0
的所有副作用都已完成。在评估三元“问号”运算符的第一个操作数与第二个或第三个操作数之间。例如,在表达式
q
中,在第一个a = (*p++) ? (*p++) : 0
之后有一个序列点,这意味着它已经在第二个实例执行时增加了。
*p++
和||
的规则对于C ++(第5.15和5.16节)与C相同。
在任何一种情况下,评估顺序是否得到保证?
是。运算符?:
,||
,&&
和,
的操作数的评估顺序保证从左到右。
答案 3 :(得分:3)
在C中,对象的存储值只能在两个序列点之间修改一次。
出现一个序列点:
&&
,||
和?:
运营商例如,此表达式x = i++ * i++
未定义,而x = i++ && i++
完全合法。
您的代码显示已定义的行为。
int x = 0;
cout&lt;&lt; (x == 0?x ++:x)&lt;&lt; ENDL;
在上面的表达式中,x
是0
,因此x++
将被执行,此处x ++是后递增的,因此它将输出0
。
cout&lt;&lt; &#34; X =&#34; &LT;&LT; x&lt;&lt; ENDL;
在上面的表达式中,x
现在具有值1
,因此输出将为1
。
cout&lt;&lt; (x == 1 || - x == 0?1:2)&lt;&lt; ENDL;
此处x
为1
,因此未评估下一个条件(--x == 0
),输出将1
。
cout&lt;&lt; &#34; X =&#34; &LT;&LT; x&lt;&lt; ENDL;
由于未评估表达式--x == 0
,输出将再次为1
。
答案 4 :(得分:2)
是的,使用增量/减量运算符是安全的。以下是您的代码中发生的事情:
cout << (x == 0 ? x++ : x) << endl; //operator in branch
在此代码段中,您正在测试x == 0
是否为true
。由于它是true
,因此您的三元表达式会评估x++
。
由于您在此处使用后增量,因此x
的原始值会打印到标准输出流,然后 x
会增加。
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
这个片段有点混乱,但它仍然会产生可预测的结果。此时,来自第一个代码段的x = 1
。在三元表达式中,首先评估条件部分;但是,由于Short-Circuiting,第二个条件, --x == 0
,永远不会被评估。
对于C ++,运算符||
和&&
分别是逻辑OR 和逻辑AND 的短路布尔运算符。使用这些运算符时,会检查您的条件(从左到右),直到确定最终结果。确定结果后,不再检查条件。
查看代码段#2,您的第一个条件会检查x == 1
。由于您的第一个条件评估为true
并且您正在使用逻辑OR,因此无需继续评估其他条件。这意味着--x == 0
从未执行。
短路对提高程序性能非常有用。 假设您有这样的条件,它会调用几个耗时的函数:
if (task1() && task2())
{
//...Do something...
}
在此示例中,除非task2
成功完成,否则永远不应调用task1
(task2
取决于task1
更改的某些数据)。
因为我们正在使用短路AND运算符,如果task1
因返回false
而失败,那么if-statement有足够的信息提前退出并停止检查其他条件。这意味着永远不会调用task2
。