我可以使用按位运算符而不是逻辑运算符吗?

Can I use bitwise operators instead of logical ones?

按位运算符作用于位,逻辑运算符计算布尔表达式。只要表达式returnbool,我们为什么不使用位运算符而不是逻辑运算符呢?

在此示例中,我使用按位而不是逻辑:

#include <iostream>


int main(){

    int age;
    std::cin >> age;

    if( (age < 0) | (age > 100) ) // eg: -50: 1 | 0 = 1
        std::cout << "Invalid age!" << std::endl;

//  if( (age < 0) || (age > 100) )
//      std::cout << "Invalid age!" << std::endl;

  return 0;
}

即使使用位运算符达到相同的结果,出于性能原因,这里最好使用逻辑运算符。

在表达式 (age < 0) || (age > 100) 中,仅当 (age < 0)false 时,才会计算第二个条件 (age > 100)。对于这样的表达式,编译器会生成如下代码:

cmpl   [=10=]x0,-0x4(%rbp)                 
js     1004010f9 <main+0x19>  // <-- Skip right expr evaluation if left true
cmpl   [=10=]x64,-0x4(%rbp)               
jle    100401100 <main+0x20>

|| 不会产生任何额外的分支来跳过第二个表达式求值。

你不应该。假设您收到两个值,只有当它们都非零时才允许您继续。

int b = foo(1); // returns 0x1
int c = foo(2); // returns 0x2

比以下条件产生以下结果:b && c == true,而 b & c == 0

if (b && c)
{
  // This block will be entered
}

if (b & c)
{
  // This block won't
}

一个可能的答案是:优化。例如:

if ((age < 0) | (age > 100))

假设 age = -5,由于满足第一个条件 (-5<0),因此无需计算 (age > 100)。但是,前面的代码会计算 (age > 100) 表达式,这不是必需的。

与:

if ((age < 0) || (age > 100))

只有第一部分会被评估。

注意:正如评论中提到的@Lundin,有时|||快第二个选项的分支准确性(以及预测错误的问题)。所以在其他表达式如此便宜的情况下, |选项 可能 会更快。因此,了解这些情况的唯一方法是在目标平台上对代码进行基准测试。


最重要的答案是避免未定义的行为和错误:

你可以想象这段代码:

int* int_ptr = nullptr;
if ((int_ptr != nullptr) & (*int_ptr == 5))

此代码包含未定义的行为。但是,如果将 & 替换为 &&,则不再存在未定义的行为。

答案是肯定的,你可以。问题是你为什么要这样做?我可以列举几个你不应该的理由:

  1. 其他程序员可能会很困惑。
  2. 很容易忽略其中一个操作数不是 bool 类型,这会导致细微的错误。
  3. 操作数计算的顺序未定义。
  4. 它打乱了短路规则。

为了说明最后一点:

bool f1() {
    cout << "f1" << endl;
    return true;
}

bool f2() {
    cout << "f2" << endl;
    return true;
}

int main() {
    if (f1() || f2()) {
        cout << "That was for ||" << endl;
    }
    if (f1() | f2()) {
        cout << "That was for |" << endl;
    }
    return 0;
}

它打印:

f1
That was for ||
f1
f2
That was for |

假设 f2 可能有显着的副作用 (if (okToDestroyWorld && destroyWorld()) ...),差异可能是巨大的。 Java 程序员(其中 ||| 实际上是由语言规范为布尔值定义的)不会感到惊讶,但这不是 C++ 中的通常做法。

我只能想到对布尔值使用按位运算符的一个原因:如果您需要 XOR。没有 ^^ 运算符,所以只要 ab 都是 bool 并且不涉及副作用,if (a ^ b) 就可以了。

||| 之间有明显的区别。

与语言中的大多数其他运算符不同,逻辑运算符 || 明确指定求值顺序。 || 的第一个操作数必须在第二个之前计算。第二个根本不需要评估。

这与大多数运算符的行为方式 | 根本不同:计算顺序未指定,两个表达式都将被计算。即使在发现一个操作数不为零的情况下,仍将评估另一个操作数的副作用。

意味着像 f1() || f2() 这样的代码将始终计算为这个伪代码:

if(f1() != 0)
{
  f2();
}

f1() | f2() 将以程序员无法知道的未指定顺序执行这两个函数。

这也意味着像 "|| 比 |" 这样的陈述是幼稚的。当然,在 || 的情况下,第二个操作数不一定被评估,但这是以分支为代价的,以及对允许编译器如何重新排序表达式的限制。哪个运算符通常更快并不明显。