8

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

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

上面的表达式是等价的吗?

4

4 回答 4

6

是的,这些表达式都是等价的,包括它们的短路行为。

&&括号改变了评估单个 s 的顺序。但是,由于&&始终是左关联的,因此始终以从左到右的顺序评估这些术语。所以一旦发现一个词是假的,剩下的就可以跳过了。

于 2011-08-26T07:48:37.573 回答
5

证明三个子表达式的两个简化情况的等价性相对容易:

a && (b && c)  -->  a && bc   // bc is a shorthand for b && c

在这里,a将首先进行评估。如果它是假的,短路将阻止评估bc。如果为真,bc就会被评估,也就是b && c会被评估。如果b为假,c则不会被评估。

(a && b) && c  -->  ab && c   // ab is a shorthand for a && b

在这里,ab将首先进行评估。(即a && b首先评估。如果a为假,短路将阻止对 的评估b。否则,ab产生b。)如果ab为假,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 ...

如您所见,除了标签之外,生成的汇编代码完全相同。

于 2011-08-26T14:35:48.483 回答
2

在您的示例中,括号无关紧要。但这只是因为&&的性质,所有条款都需要检查(如果为真,或者如果其中任何一个为假,则为假)。

在这个例子中,括号确实有很大的不同:

(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 && d)。
在第二行中,如果 a 为假,它只会返回假。

于 2011-08-26T07:55:41.407 回答
2

此属性称为关联性。来自维基百科文章

在数学中,结合性是一些二元运算的属性。这意味着,在同一关联运算符的一行中包含两次或多次出现的表达式中,只要操作数的顺序不变,执行操作的顺序无关紧要。也就是说,在这样的表达式中重新排列括号不会改变它的值。

内置operator&&是完全关联的,因此上述适用。

情况并非总是如此,例如:

  • operator-通常是左结合的,即a - b - c == (a - b) - c != a - (b - c)
  • 取幂是右结合的,即a ** b ** c == a ** (b ** c) != (a ** b) ** c
  • cross-product 是non-associative,也就是说(a x b) x c != a x (b x c)(没有括号,表达式甚至没有意义)

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

于 2011-08-26T08:47:39.597 回答