短路和括号

时间:2011-08-26 07:46:04

标签: c++ operators evaluation short-circuiting parentheses

在处理单个短路运算符时,如何对子表达式进行分组是否重要?

a && b && c && d
a && (b && (c && d))
(a && b) && (c && d)
((a && b) && c) && d

上述表达式是否相同?

4 个答案:

答案 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)(没有括号,表达式甚至没有意义)

请注意,这仅适用于一致使用单个运算符的情况,只要在混合中引入了另一个运算符(如||),那么您必须考虑运算符优先级,是另一个话题。