在C ++中对布尔值使用按位运算符

时间:2020-03-05 18:42:28  来源:igfitidea点击:

是否有任何理由不对C ++中的"布尔"值使用按位运算符&,|和^?

有时我遇到两种情况,我希望精确地满足两个条件之一(XOR),因此我只是将^运算符放入条件表达式中。有时我还希望评估条件的所有部分,无论结果是否正确(而不是短路),因此我使用&和|。有时我还需要累积布尔值,并且&=和| =可能非常有用。

在执行此操作时,我有些挑剔,但是代码仍然比其他代码有意义且更简洁。有没有理由不将其用于布尔?是否有任何现代编译器为此带来不好的结果?

解决方案

回答

扬起的眉毛应该告诉你足以停止这样做。我们无需为编译器编写代码,而是先为其他程序员编写代码,然后再为编译器编写代码。即使编译器可以工作,令人惊讶的是,其他人也不是我们想要按位运算符用于位运算而不是布尔运算的。
我想你也用叉子吃苹果吗?它可以工作,但会让人们感到惊讶,因此最好不要这样做。

回答

||&&是布尔运算符,并且内置的运算符保证返回true或者false。没有其他的。

|^是按位运算符。当我们操作的数字的域仅是1和0时,它们是完全相同的,但是在布尔值不严格地为1和0的情况下(如C语言那样),我们可能会遇到某些错误不想。例如:

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

但是,在C ++中,保证"布尔"类型只能是"真"或者"假"(它们分别隐式地转换为" 1"和" 0"),因此不必担心这种情况,但是人们不习惯在代码中看到这样的事实,为不这样做提供了很好的理由。只需说出'b = b && x`即可完成。

回答

帕特里克(Patrick)提出了要点,我不再重复。但是我可能会建议通过使用命名良好的布尔变量来尽可能地将'if'语句简化为可读的英语,例如,这是使用布尔运算符,但我们也可以使用按位并适当地命名布尔值:

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'语句的条件(尽管这似乎很有可能),这是我的假设。但是我对中间布尔值的建议仍然存在。

回答

我认为

a != b

是你想要的

回答

IIRC,许多C ++编译器会在尝试将按位运算的结果强制转换为布尔值时发出警告。我们必须使用类型强制转换才能使编译器满意。

在if表达式中使用按位运算将引起相同的批评,尽管编译器可能不会这样做。任何非零值都将被视为true,因此" if(7&3)"之类的值将为true。这种行为在Perl中是可以接受的,但是C / C ++是非常明确的语言。我认为Spock的眉毛是尽职调查。 :)我会添加" == 0"或者"!= 0"以使其完全清楚目标是什么。

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

回答

两个主要原因。简而言之,请仔细考虑。这样做可能有充分的理由,但是如果注释中有非常明确的内容,因为它可能很脆弱,而且就像我们自己说的那样,人们通常不习惯看到这样的代码。

按位异或者!=逻辑异或者(0和1除外)

首先,如果我们对除false和true以外的值(或者整数为0和1的值)进行运算,则^运算符可以引入不等于逻辑异或者的行为。例如:

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(),或者其他人可能后来出现并在以后不理解依赖项的情况下对其进行了更改,从而导致了讨厌的(通常仅是发布构建)错误。

回答

与Patrick的答案相反,C ++没有用于执行短路异或者的^^运算符。如果再想一想,使用^^运算符将毫无意义:使用"异或者"运算符,结果始终取决于两个操作数。但是,在将" 1&2"与" 1 && 2"进行比较时,Patrick关于非布尔型"布尔"类型的警告同样适用。一个经典的例子就是Windows的GetMessage()函数,该函数返回三态的BOOL:非零,0或者-1.

使用代替&&以及|代替||并不是常见的错别字,因此,如果我们故意这样做,则应添加注释以说明原因。