【问题标题】:Short Circuiting Logical OR Behavior短路逻辑或行为
【发布时间】:2024-04-14 18:45:01
【问题描述】:

我的印象是,在 C 语言中,逻辑 OR 运算符|| 是一个短路运算符,如果 lhs 为假,则不会计算 rhs。我在比较 OR'ed 值时遇到了问题。有人可以向我解释为什么以下代码(在 gcc 5.3 上)评估为真吗?我在 clang 中遇到了同样的行为。

#include <stdio.h>

int main() {

    int attr = 2;

    if( ((attr & 2) != 2) || ((attr & 4) != 4) ) {
        printf("No short circuit?\n");
    };

    printf("%i %i\n",attr,(attr & 2));

};

输出:

No short circuit?

2 2

【问题讨论】:

  • 你的印象是错误的。如果 LHS 为假,它会尝试 RHS。
  • that doesnt evaluate the rhs if the lhs is false,这根本没有任何意义。它与逻辑或应该是相反的。您的陈述对于逻辑 AND 是正确的。
  • "逻辑 OR 运算符 || 是一个短路运算符,如果 lhs 为假,则不计算 rhs" 休息一下! ;-)

标签: c gcc clang


【解决方案1】:

((attr &amp; 2) != 2) || ((attr &amp; 4) != 4) 计算结果为真,因为

  1. attr &amp; 4 == 0(attr &amp; 4) != 41,因为 attr &amp; 4 不等于 4。 (N1570 6.5.9 等式运算符)
  2. 因为至少有一个操作数不为零,所以表达式的计算结果为1。 (N1570 6.5.14 逻辑或运算符)
  3. if 语句在控制表达式不为零时执行第一个子语句(N1570 6.8.4.1 if 语句),您将把它称为“计算为真的表达式”。

如果 lhs 为 true,则逻辑 OR 运算符不会计算 rhs,因为在 rhs 为真或假的两种情况下,该值都将为真。

如果 lhs 为假,逻辑 AND 运算符不会计算 rhs,因为在 rhs 为真或假的两种情况下,该值都将为假。

引用N1570:

6.5.13 逻辑与运算符

[...]

3 如果 && 运算符的两个操作数比较不等于 0,则 && 运算符将产生 1;否则,它 产生 0。结果的类型为 int。

4 与按位二进制 & 运算符不同,&& 运算符保证从左到右的求值; 如果对第二个操作数求值,则在求值之间存在一个序列点 第一个和第二个操作数。如果第一个操作数比较等于 0,则第二个 不计算操作数。

6.5.14 逻辑或运算符

[...]

3 ||如果任一操作数比较不等于 0,则运算符应产生 1;否则,它 产生 0。结果的类型为 int。

4 不同于按位 |运算符,||运算符保证从左到右的评估;如果 第二个操作数被求值,第一个求值之间有一个序列点 和第二个操作数。如果第一个操作数比较不等于 0,则第二个操作数为 未评估。

【讨论】:

  • 啊,好吧……这很有道理。我的印象是,如果 LHS 是假的,它会短路,但我看到它与 && 相反。感谢您清除迷雾!
  • 我知道第二部分会是真的......我最初认为如果第一部分是假的(&& 行为),第二部分就不会被评估。更令人困惑的 C 语义存储在我的脑海中! :)
【解决方案2】:

您拥有的代码:

((attr & 2) != 2) || ((attr & 4) != 4) )

会发生什么:

  1. (attr &amp; 2) 计算结果为 2。(0010 AND 0010 = 0010 即 2)
  2. (2 != 2) 的计算结果为 0,或为假。
  3. 现在 OR 的右侧被评估(如果左侧为真,那么右侧将不会被执行/评估,即“短路”。但在此示例中它是错误的,所以右侧是评估)。
  4. (attr &amp; 4) 是按位与运算,例如0010 和 0100。计算结果为 0000 或 0。
  5. (0 != 4) 的计算结果为 1 或 true,因此执行 if 语句的主体。
  6. 然后程序的其余部分(最后一条语句)执行,程序终止。

记住:

  • &amp; 是按位与。
  • &amp;&amp; 是布尔 AND。
  • | 是按位或。
  • || 是布尔或。

【讨论】:

    【解决方案3】:

    这里有两种方法可以让你在 C 中获得短路行为:

    unsigned int bitwise_logical_or(unsigned int p, unsigned int q) {
       return (p | (q & ((p != 0) - 1))); 
    }
    
    unsigned int numerical_logical_or(unsigned int p, unsigned int q) {
       return p + (p == 0) * q;
    }
    

    正如其他答案中提到的,|| 运算符返回 true (0x01) 或 false (0x00)。

    bitwise_logical_or 工作原理如下:

    如果对整数 p 和 q 执行按位或运算,则仅当 p 为零时才会起作用。如果 p 不为零,则按位或将以不可预测的方式混合 p 和 q。如果 q 可以设置为零,如果 p 大于零,我们将得到我们想要的结果。因此,当 p 不为零时,我们希望在 q 和零之间执行按位和。诀窍是简单地测试 p == 0 将返回(8 位)0x00 或 0x01。我们需要它适当地返回 0xff 或 0x00。我们通过检查 p != 0 来实现这一点,这将在 p > 0 时返回 0x01,当 p == 0 时返回 0x00。减去 1,在 p > 0 的情况下,你得到 0x00(假)。当 p == 0 时,您已经有 0x00(全为零),没有可去处,所以整数回绕(下溢)并且您得到 0xff(全为 1)。

    【讨论】:

      最近更新 更多