【问题标题】:Is there any difference between && and & with bool(s)?&& 和 & 与 bool(s) 之间有什么区别吗?
【发布时间】:2011-07-05 02:24:46
【问题描述】:

在 C++ 中,在 bool(s) 之间执行 &&(逻辑)和 &(按位)之间有什么区别吗?

bool val1 = foo();
bool val2 = bar();

bool case1 = val1 & val2;
bool case2 = val1 && val2;

case1 和 case2 是否相同,或者如果不同,它们究竟有什么不同?为什么要选择一个而不是另一个?按位和布尔值可移植吗?

【问题讨论】:

  • 除了编写清晰的代码外,转换 boolint 可能是一个性能问题。
  • 我认为 case1 和 case2 的值相同,并且 bools 的按位与是可移植的。
  • 只是为了了解更多信息,实现定义的运算符之一(根据 K&R 书)是有符号值的右移。
  • 您的示例使它们相同。但是将它们称为foo() & bar() 和foo() && bar() 会产生不同的效果——在后一种情况下,如果 foo() 返回 false,bar()(可以在返回 bool 之上启动电梯)将永远不会执行。跨度>
  • 相关:Boolean values as 8 bit in compilers. Are operations on them inefficient? re:一些实际编译器遗漏的优化,如 GCC、clang 和 MSVC。以及他们成功的案例。

标签: c++ bitwise-operators boolean logical-operators language-lawyer


【解决方案1】:

standard 保证 false 转换为 0,true 转换为 1 作为整数:

4.7 积分转换

...

如果目标类型是 bool,请参见 4.12。如果源类型为 bool,则值 false 转换为 0,值 true 转换为 1。

所以你给出的例子中的效果保证是一样的,是100%可移植的。

对于您给出的情况,任何体面的编译器都可能生成相同的(最佳)代码。

但是,对于布尔表达式expr1 和expr2,通常expr1 && expr2 与expr1 & expr2 相同是不正确的,因为&& 执行“短路”评估。也就是说,如果expr1 计算为false,则expr2 甚至不会被计算。这会影响性能(如果expr2 很复杂)和行为(如果expr2 有副作用)。 (但请注意,& 表单如果避免条件分支实际上可以更快......出于性能原因玩弄这类事情几乎总是一个坏主意。)

因此,对于您给出的具体示例,您将值加载到局部变量中然后对其进行操作,行为是相同的,性能很可能是。

在我看来,除非您特别依赖“短路”行为,否则您应该选择最清楚表达您的意图的表述。因此,使用&& 进行逻辑与,使用& 进行位旋转AND,任何有经验的C++ 程序员都会发现你的代码很容易理解。

【讨论】:

  • 你的论点适用吗?到 |与 ||谢谢。
  • @PatrickT:是的,出于同样的原因。 (“OR”和“AND”是对偶运算;也就是说,如果将“OR”与“AND”交换,将“false”与“true”交换,则所有等式仍然成立。换句话说,OR和AND实际上是相同的操作,只是生活在正逻辑和负逻辑的世界中。它们在 C/C++ 中的实现尊重这种相似性,这被称为“德摩根定律”。)
【解决方案2】:

使用逻辑和&& 时,如果左侧表达式为假,则不会计算右侧表达式。

很多 C/C++/C# 代码都依赖于此,例如:if (p != null && p->Foo())。

对于您的示例,我将使用 case2(逻辑与)。仅在处理位标志等时使用按位。

但是,如果 foo() 和 bar() 只返回 bool(0, 1) 那么 case1 和 case2 是一样的。

【讨论】:

    【解决方案3】:

    有一个区别(嗯,两个),虽然你不会在你的例子中看到它。

    "&" 执行按位“与”运算,即0x1 & 0x1 = 0x1,但0x1 & 0x2 = 0x0。 OTOH,“&&”是一个布尔/逻辑“AND”,意味着它将任何非零值视为 TRUE,因此0x1 && 0x1 = TRUE(通常表示为 -1,即全为 [或者它可能表示为 1在 C++ 中,我忘记了]),而 0x1 && 0x2 = TRUE 也是如此。

    此外,“&&”是短路的,这意味着如果第一个操作数为 FALSE,则不会计算第二个操作数。所以,虽然FALSE & null_pointer->booleanField ==> null pointer exception,FALSE && null_pointer->booleanField = FALSE。

    在某些情况下使用按位运算可能会有轻微的性能优势,但通常在评估布尔值时应该使用双精度形式,这样您的代码就独立于布尔值 TRUE 和 FALSE 的精确表示。

    【讨论】:

    • 是否存在值为 0x2 的布尔值或者标准禁止的值(甚至是无意义的问题)?
    • C/C++ 标准规定 true 的整数值为 1。但是,例如,如果您调用返回布尔值的方法,则始终存在实现它的人没有阅读的危险标准。
    • 我刚刚在野外遇到了0x1 & 0x2 = 0x0 案例。我有一个函数 funct1() 应该返回 true,但实际上没有 return 语句。不知道为什么这没有产生编译器错误,但它实际上返回了一个未定义的整数转换为布尔值。因此,当我执行bool ok = funct1(); ok &= funct2(); 时,我得到了不正确的结果,因为 ok 在监视窗口中的第一个语句之后似乎为真,但实际上是一个偶数,当第二个语句中的实际“真”按位与运算时变为假.
    【解决方案4】:

    在算法上没有区别,但是使用 && 可以让您“短路”检查。也就是说,要判断 case2,如果 val1 为假,那么编译后的代码没有理由检查 val2 的值以确定答案,而 case1 需要进行实际的 AND。

    实际上,一个好的编译器会识别这一点并生成相同的代码......这取决于你的编译器有多好。

    【讨论】:

      【解决方案5】:

      "&&" 是一个“条件逻辑“与” 仅当第一个表达式为 TRUE 时才计算第二个表达式

      "&" 是一个“非条件逻辑与”


      更多 “&”是一个“位”运算符,这意味着它在位级别上运行。

      这个例子可以让你更好地理解。

      4 = 00000100  // 'four' bit set
      5 = 00000101  // 'four' bit and 'one' bit set
      
      00000100 (4) & // AND: only keep bits set in both
      00000101 (5)
      --------
      00000100 (4)
      
      00000100 (4) | // OR: keep bits set in either
      00000101 (5)
      --------
      00000101 (5)
      
      00000100 (4) ^ //  EXCLUSIVE OR: keep bits only set in one but not the other
      00000101 (5)
      --------
      00000001 (1)
      

      【讨论】:

        【解决方案6】:

        逻辑运算符 && 和 ||在评估两个表达式以获得单个关系结果时使用。运算符 && 对应于布尔逻辑运算 AND。如果两个操作数都为真,则此运算结果为真,否则为假。

        运算符 ||对应于布尔逻辑运算 OR。如果两个操作数中的任何一个为真,则此操作结果为真,因此只有当两个操作数本身都为假时才为假。

        【讨论】:

          【解决方案7】:
          int a = 0, b = 10;
          
          if(a == 1 && (b/a) == 0) cout << "This is okay\n"; // the 2nd expression is never checked as the first one is false
          
          cout << "Bingo\n";
          
          if(a == 1 & (b/a) == 0) cout << "This is not okay\n"; // program crashes here trying to divide by zero
          
          cout << "Bingo\n"; // this will never get printed
          

          【讨论】:

          • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
          猜你喜欢
          • 2015-02-23
          • 2011-03-02
          • 2011-09-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-17
          • 2014-02-14
          相关资源
          最近更新 更多