在处理单个短路运算符时,如何对子表达式进行分组是否重要?
a && b && c && d
a && (b && (c && d))
(a && b) && (c && d)
((a && b) && c) && d
上述表达式是否相同?
答案 0 :(得分:5)
是的,这些表达式都是等效的,包括它们的短路行为。
括号会更改评估单个&&
的顺序。但是,由于&&
始终是左关联的,因此始终按从左到右的顺序评估这些术语。因此,只要发现某个术语为假,其余部分就可以跳过。
答案 1 :(得分:5)
证明三个子表达式的两个简化情况的等价性相对容易:
a && (b && c) --> a && bc // bc is a shorthand for b && c
此处,将首先评估a
。如果为假,短路将阻止bc
的评估。如果是,则将评估bc
,即将评估b && c
。如果b
为false,则不会评估c
。
(a && b) && c --> ab && c // ab is a shorthand for a && b
此处,将首先评估ab
。 (首先评估a && b
。如果a
为假,短路将阻止评估b
。否则,ab
会产生b
。)如果ab
为false,c
将不会被评估。
现在,如果您更喜欢证据来证明,您可以查看以下C代码的汇编输出:
int a(), b(), c(), d();
void e()
{
a() && b() && c() && d();
}
void f()
{
a() && (b() && (c() && d()));
}
void g()
{
(a() && b()) && (c() && d());
}
void h()
{
((a() && b()) && c()) && d();
}
(我使用C代码而不是C ++代码来防止名称损坏。)
生成e
的汇编:
_e:
// ... enter ...
call _a
testl %eax, %eax
je L1
call _b
testl %eax, %eax
je L1
call _c
testl %eax, %eax
je L1
call _d
testl %eax, %eax
nop
L1:
// ... leave ...
生成f
的汇编:
_f:
// ... enter ...
call _a
testl %eax, %eax
je L4
call _b
testl %eax, %eax
je L4
call _c
testl %eax, %eax
je L4
call _d
testl %eax, %eax
nop
L4:
// ... leave ...
生成g
的汇编:
_g:
// ... enter ...
call _a
testl %eax, %eax
je L7
call _b
testl %eax, %eax
je L7
call _c
testl %eax, %eax
je L7
call _d
testl %eax, %eax
nop
L7:
// ... leave ...
生成h
的汇编:
_h:
// ... enter ...
call _a
testl %eax, %eax
je L10
call _b
testl %eax, %eax
je L10
call _c
testl %eax, %eax
je L10
call _d
testl %eax, %eax
nop
L10:
// ... leave ...
如您所见,除标签外,生成的汇编代码完全相同。
答案 2 :(得分:2)
在您的示例中,括号无关紧要。但这只是因为&amp;&amp; <和strong>的性质,需要检查所有条款(如果为真,或者如果其中任何一个为假,则为假)。
在这个例子中,括号确实有很大的不同:
(a && b) || (c && d) // either a & b are true, or c & d
a && (b || c && d) // a must be true, and either b or c & d
(a && b || c) && d // d must be true, and either c or a & b
当然,由于逻辑不同,短路的工作方式不同 如果a为假,则在第一行中,它将继续到第二项(c&amp; d) 如果a为false,则在第二行中,它将返回false。
答案 3 :(得分:2)
此属性称为 Associativity 。来自Wikipedia Article:
在数学中,关联性是一些二元运算的属性。这意味着,在同一个关联运算符的一行中包含两个或更多个出现的表达式中,只要操作数的顺序没有改变,执行操作的顺序就无关紧要了。也就是说,在这样的表达式中重新排列括号不会改变其值。
内置operator&&
是完全关联的,因此上述情况适用。
情况并非总是如此,例如:
operator-
通常是左关联,即a - b - c == (a - b) - c != a - (b - c)
a ** b ** c == a ** (b ** c) != (a ** b) ** c
(a x b) x c != a x (b x c)
(没有括号,表达式甚至没有意义)请注意,这仅适用于一致使用单个运算符的情况,只要在混合中引入了另一个运算符(如||
),那么您必须考虑运算符优先级,是另一个话题。