【问题标题】:short circuiting and parenthesis短路和括号
【发布时间】:2023-03-06 04:02:02
【问题描述】:

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

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

以上表达式是否等价?

【问题讨论】:

  • 在我上次的评论中我错了,对此我深表歉意。我删除了它,因为它不正确。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案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 为 false,它只会返回 false。

    【讨论】:

      【解决方案3】:

      此属性称为关联性。来自Wikipedia Article

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

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

      并非总是如此,例如:

      • operator- 一般是left-associative,即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)(没有括号,表达式甚至没有意义)

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

      【讨论】:

      • 这解决了&& 的数学定义的关联性,但它没有解决短路问题(不是数学定义的一部分)。
      • @Oli:引用并没有解决它,但是当我将operator&& 限定为完全关联时,我会这样做。如果不保持短路行为,那么我们就不能将运算符限定为关联运算符。
      • 根据 C 运算符的大多数描述,&& 是左关联的。公平地说,我真的不知道这意味着什么,因为没有其他运算符具有与它相同的优先级。
      • @Oli:左关联可能意味着a && b && c == (a && b) && c != a && (b && c),我会说这是错误的,或者它可能意味着a && b && c == (a && b) && c 而忘记右关联。在评估a && (b && c) 时,如果a 为假,则它停止,否则它评估b && c,如果b 为假则短路,否则评估c。因此&& 是完全关联的,您可能会说它是左关联的(虽然不是严格意义上的)。
      • 我将“左关联”解释为暗示a && b && c == (a && b) && c。它并不一定意味着!= a && (b && c)
      【解决方案4】:

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

      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-11-11
        • 1970-01-01
        • 2015-11-16
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        • 2021-01-11
        • 1970-01-01
        • 2019-02-03
        相关资源
        最近更新 更多