在C ++中使用按位运算符进行布尔运算

时间:2008-08-23 19:52:40

标签: c++ boolean bitwise-operators

有没有理由不在C ++中使用按位运算符&,|和^作为“bool”值?

我有时遇到两种情况中只有一种是真的(XOR)的情况,所以我只是将^运算符抛出到条件表达式中。我有时也希望评估条件的所有部分是否真实(而不是短路),所以我使用&和|。我有时也需要积累布尔值,而且& =和| =非常有用。

这样做时我已经有了一些眉毛,但代码仍然有意义且比其他方式更清晰。是否有任何理由不将这些用于布尔?是否有任何现代编译器可以为此提供不良结果?

9 个答案:

答案 0 :(得分:48)

||&&是布尔运算符,内置函数保证返回truefalse。没别了。

|&^是按位运算符。当你操作的数字域只有1和0时,它们完全相同,但是如果你的布尔值不是严格的1和0 - 就像C语言一样 - 你可能最终会遇到一些行为你不想要。例如:

BOOL two = 2;
BOOL one = 1;
BOOL and = two & one;   //and = 0
BOOL cand = two && one; //cand = 1

但是,在C ++中,bool类型只能保证truefalse(隐式转换为10因此,这种立场并不是一种担心,但是人们不习惯在代码中看到这样的事情这一事实对于不这样做是一个很好的理由。只需说出b = b && x并完成它。

答案 1 :(得分:25)

两个主要原因。总之,要仔细考虑;这可能是一个很好的理由,但如果你的评论中有非常明确的原因,因为它可能很脆弱,正如你自己所说,人们通常不习惯看到这样的代码。

按位xor!=逻辑xor(0和1除外)

首先,如果您使用falsetrue(或01以外的值作为整数运算,^运算符可以引入的行为不等同于逻辑xor。例如:

int one = 1;
int two = 2;

// bitwise xor
if (one ^ two)
{
  // executes because expression = 3 and any non-zero integer evaluates to true
}

// logical xor; more correctly would be coded as
//   if (bool(one) != bool(two))
// but spelled out to be explicit in the context of the problem
if ((one && !two) || (!one && two))
{
  // does not execute b/c expression = ((true && false) || (false && true))
  // which evaluates to false
}

感谢用户@Patrick首先表达了这一点。

运营顺序

其次,|&^作为按位运算符,不会发生短路。此外,在单个语句中链接在一起的多个按位运算符 - 即使使用明确的括号 - 也可以通过优化编译器进行重新排序,因为所有3个操作通常都是可交换的。如果操作的顺序很重要,这很重要。

换句话说

bool result = true;
result = result && a() && b();
// will not call a() if result false, will not call b() if result or a() false

并不总是给出与

相同的结果(或结束状态)
bool result = true;
result &= (a() & b());
// a() and b() both will be called, but not necessarily in that order in an
// optimizing compiler

这一点尤其重要,因为您可能无法控制方法a()b(),或者其他人可能会出现并在以后更改它们而不理解依赖关系,并导致讨厌(并且经常发布 - 构建只有)bug。

答案 2 :(得分:11)

我认为

a != b

是你想要的

答案 3 :(得分:9)

凸起的眉毛应该足以让你停止这样做。您不编写编译器的代码,首先为其他程序员编写代码,然后编写代码。即使编译器工作,令人惊讶的是其他人不是你想要的 - 按位运算符不是用于bool的位操作。
我想你也用叉子吃苹果?它有效,但它给人们带来惊喜,所以最好不要这样做。

答案 4 :(得分:6)

bitlevel运算符的缺点。

你问:

  

“有没有理由不在C ++中使用按位运算符&|^作为”bool“值? “

是的,逻辑运算符,即内置的高级布尔运算符!&&||,具有以下优势: / p>

  • 保证将参数转换为bool,即01序数值。

  • 保证短路评估,一旦知道最终结果,表达式评估就会停止。
    这可以解释为树值逻辑, True False Indeterminate

  • 可读的文字对等notandor,即使我自己也不使用它们。
    正如读者锑在评论中指出的那样,位级运算符也有替代标记,即bitandbitorxorcompl,但在我看来,它们的可读性低于and。 {1}},ornot

简单地说,高级运算符的每个优点都是位级运算符的缺点。

特别是,由于按位运算符缺少参数转换为0/1,因此1 & 20,而1 && 2true。同样^,按位排他,或者可能以这种方式行为不端。被视为布尔值1和2是相同的,即true,但被视为位图,它们是不同的。

<小时/>

如何在C ++中表达逻辑 /或

然后,您提供了一些问题的背景知识,

  

“我有时遇到两种情况中只有一种是真实的情况(XOR),所以我只是将^运算符抛出一个条件表达式。”

嗯,按位运算符的优先级高于逻辑运算符。这尤其意味着在混合表达中,例如

a && b ^ c

你得到了意想不到的结果a && (b ^ c)

而只是写

(a && b) != c

更简明扼要地表达你的意思。

对于多个参数或/或,没有C ++运算符可以完成这项工作。例如,如果您编写a ^ b ^ c而不是表达“abc为真”的表达式。相反,它说,“奇数abc都是真的”,可能是其中一个或全部3个......

要表达一般性/或abc属于bool,只需写一下

(a + b + c) == 1

或使用非bool参数,将它们转换为bool

(!!a + !!b + !!c) == 1

<小时/>

使用&=累积布尔结果。

你进一步详细说明,

  

“我有时也需要积累布尔值,&=|=?非常有用。”

嗯,这对应于检查是否满足所有任何条件, de Morgan定律告诉您如何从一个到另一个。即你只需要其中一个。你原则上可以使用*=作为&&= - 运算符(对于旧的George Boole发现,逻辑且可以很容易地表示为乘法),但我认为那会困扰并可能误导维护者代码。

还要考虑:

struct Bool
{
    bool    value;

    void operator&=( bool const v ) { value = value && v; }
    operator bool() const { return value; }
};

#include <iostream>

int main()
{
    using namespace std;

    Bool a  = {true};
    a &= true || false;
    a &= 1234;
    cout << boolalpha << a << endl;

    bool b = {true};
    b &= true || false;
    b &= 1234;
    cout << boolalpha << b << endl;
}

使用Visual C ++ 11.0和g ++ 4.7.1输出:

true
false

结果不同的原因是位等级&=未向其右侧参数bool提供转换。

那么,您希望使用&=

中的哪些结果?

如果前者true,则更好地定义运算符(例如,如上所述)或命名函数,或使用右侧表达式的显式转换,或者完整地写入更新。

答案 5 :(得分:3)

与Patrick的回答相反,C ++没有^^运算符用于执行短路异或。如果你考虑它一秒钟,拥有^^运算符无论如何都没有意义:使用exclusive或者,结果总是取决于两个操作数。但是,在将bool1 & 2进行比较时,Patrick对非1 && 2“布尔”类型的警告同样适用。一个典型的例子是Windows GetMessage()函数,它返回一个三态BOOL:非零,0-1

使用&代替&&|代替||并不是一个罕见的拼写错误,所以如果您故意这样做,那么值得发表评论说明原因。

答案 6 :(得分:2)

帕特里克提出了很好的观点,我不打算再重复一遍。但是我可以建议尽可能通过使用名称很好的布尔变量将'if'语句减少到可读的英语。例如,这是使用布尔运算符,但你可以同样使用按位并适当地命名bool:

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
 .. stuff ..
}

您可能认为使用布尔值似乎没必要,但它有两个主要内容:

  • 您的代码更容易理解,因为'if'条件的中间布尔值使条件的意图更明确。
  • 如果您使用非标准或意外的代码,例如布尔值上的按位运算符,人们可以更容易地了解您为什么这样做。

编辑:你没有明确表示你想要'if'语句的条件(虽然这似乎很可能),这是我的假设。但我对中间布尔值的建议仍然存在。

答案 7 :(得分:0)

IIRC,许多C ++编译器在尝试将按位操作的结果转换为bool时会发出警告。您必须使用类型转换才能使编译器满意。

在if表达式中使用按位运算会产生同样的批评,尽管可能不是编译器。任何非零值都被认为是真的,所以像“if(7&amp; 3)”这样的东西都是真的。在Perl中这种行为是可以接受的,但C / C ++是非常明确的语言。我认为Spock的眉毛是尽职尽责的。 :)我会追加“== 0”或“!= 0”来清楚地说明你的目标是什么。

但无论如何,这听起来像是个人偏好。我会通过lint或类似的工具运行代码,看看它是否也认为这是一个不明智的策略。就个人而言,它看起来像编码错误。

答案 8 :(得分:0)

对bool使用按位运算有助于节省处理器不必要的分支预测逻辑,这是由于cmp&#39;逻辑运算引入的指令。

用位操作替换逻辑(其中所有操作数都是bool)生成更高效的代码,提供相同的结果。理想情况下,效率应该超过使用逻辑运算在订购中可以利用的所有短路优势。

这可能会使代码有点不可读,尽管程序员应该对其进行评论,理由是为什么这样做。