什么时候在条件表达式中使用按位运算符合适?


15

首先,有一些背景知识:我是一名IT培训师,我想在我的10年级课程中介绍Java的布尔运算符。我的导师查看了我准备的工作表,并评论说我可以让他们只使用一个&或|。表示操作员,因为他们“做同样的事情”。

我知道&和&&之间的区别。
&是按位运算符,旨在在整数之间使用,以执行“位旋转”。
&&是旨在在布尔值之间使用的条件运算符。

为了证明这些运算符并不总是“做相同的事情”,我着手寻找一个示例,在该示例中,使用布尔值之间的按位运算会导致错误。我找到了这个例子

boolean bitwise;
boolean conditional;
int i=10, j=12;
bitwise = (i<j) | ((i=3) > 5); // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i<j) || (i=3) > 5 ;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);
i=10; 
bitwise = (i>j) & (i=3) > 5;   // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i>j) && (i=3) > 5;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);

此示例表明,如果必须在表达式的后半部分更改值,则这将导致结果之间的差异,因为按位运算符是一个急切的运算符,而条件语句则表现为短路(不评估第二个如果前半部分在&&情况下为false,在||情况下为true,则为一半。

我对这个例子有疑问。为什么要在比较值的同时更改值?这似乎不是一种健壮的编码方式。我一直不愿意在生产代码中在一行中执行多项操作。看起来像一个“编码牛仔”,对他的代码的可维护性没有良知。我知道在某些领域中代码需要尽可能的紧凑,但可以肯定这是一个不好的做法吗?

我可以解释一下鼓励使用&&和||的选择。在&和| 因为这是软件工程中公认的编码惯例

但是有人可以给我一个更好的,甚至是真实世界的在条件表达式中使用按位运算符的示例吗?

Answers:


16

在执行遮罩操作时比较合适

如果((a&b)> 0){...}

其中a和b是整数

|| 和 和&&和&不可互换

| 和&绝不会单独出现在条件表达式中(您所包含的链接的要点是,此类错误通常是错误的)

编辑:不要与您的导师争论;即使你赢了,你也输了。相反,通过在同一课中混合使用逻辑运算符和按位运算符来说明您不想使学生感到困惑。您可以解释,如果i = 3和j = 2,则i&j = 2,而i && j是错误。但是,更简单的解释是您正在教布尔(逻辑)运算符,因此抛出特殊情况下的按位等效项会分散课程的重点。不需要将导师“弄错”,也不需要产生反例。本课的重点是布尔运算符,而不是按位运算符。

因此,当您开始教按位运算符时,无需显示+和-产生与&和|相同结果的特殊情况。


上面的代码虽然没有错,但可能会造成混淆,但就目前的情况来看,该代码不包含错误吗?我同意以这种方式使用按位运算符不是很容易理解,如果我在代码审查中看到它,我会不喜欢它,但是它是合法的Java(也包括C#,而忽略了System.out.println)。
史蒂夫

感谢您回答@Steven A. Lowe。你说:“||和|和&&和&不能互换”,但bitwise = !(true & true == false);condition = !(true && true == false);都将在这种情况下,他们是可以互换的评价为真,这样的吗?从语法上讲,因为代码仍然可以编译。我同意它们在语义上用于不同的事物,正如我在第2段中提到的。和&“几乎永远不会出现在有条件的情况下”。我正在寻找这些“几乎从不”的案例,并想知道它们是否确实存在。
Deerasha 2011年

@Deerasha:|| 和&& 仅对布尔值进行运算。&和| 对整数布尔值进行操作-使用按位运算符(打算对多个位进行操作)来操作布尔值毫无意义,而放弃这样做可能会导致代码混乱和意外行为(即,如果您不小心使用整数而不是整数)布尔值,按位运算符将不会抱怨)
Steven A. Lowe

是@Steve Haigh,编译器不会拒绝它,但是从我链接到的已发布编码标准来看,这不是使用它们的正确方法。我可以放心地拒绝它,因为它不符合编码标准,还是应该用java指出这是不正确的用法?
Deerasha

2
@Steven A. Lowe:如果i = 3且j = 2,则i&j = 2,而i && j是一个错误,这真是太好了!这很简单,很重要。在10年级,这也是一个可能犯的错误,因为它们仍然习惯于布尔类型以及运算符将应用于的内容。非常感谢!也不要与我的导师争论的好建议。
Deerasha

12

非按位运算符&&||是短路运算符。换句话说,使用&&,如果LHS为假,则永远不会评估RHS;与||如果LHS是真实的,那么RHS将永远不会被评估。另一方面,按位运算符&|是非短路的,将始终评估LHS和RHS。否则,它们在if语句中是等效的。

我唯一看到使用非短路算子的价值是,在所有情况下RHS是否都具有某种理想的副作用。我想不出一个具体的例子来说明这个问题,我也不认为这是个好习惯,但这是有区别的。


1
+1。没错,当比较布尔值和逻辑运算符时,总是返回相同的结果,但是(至少在Java和C#中)只有逻辑运算符会短路。
史蒂夫

感谢您的回答。您的第1段是我试图在第4段中得到的内容,它指出按位运算是一个急切的运算符,而条件语句则表现为短路。您的第2段是我在第5段中描述的关注点。因此,我知道两者之间的区别,但是我正在寻找您和我都无法想到的特定示例。
Deerasha

7

一般的哲学答案是对布尔运算符使用按位运算符是非典型的,这会使代码更难阅读。在实践中(对于生产中的代码),可读性更高的代码更易于维护,因此更可取。

对于现实中对短路操作员的需求,请参阅以下案例:

if (args.length > 0 && args[0] == 'test') ....

if (b != NULL && b.some_function()) ...

if (b == NULL || b.some_function()) ...

这些类型的操作经常出现在实际代码中。


茶 我如何向15岁的孩子解释1 &比2更“难以阅读”?我没有一个示例,其中2个运算符对布尔操作数的工作方式不同。我同意您的观点,即更具可读性和易于维护的代码。我想鼓励他们编写漂亮的代码。但是,比起“因为我这样说”,在我的工具包中包含一些证据会更具说服力。我确实将其声明为该链接的标准,并且如果我没有找到我要寻找的示例,则可能不得不单独依靠它。就像我问@Steve Haigh一样:java是否应该指出这是不当使用?
Deerasha 2011年

@Deerasha我在想更多与您的导师争论的话题。对于该类,甚至不要试图告诉他们可以对逻辑条件使用按位运算符。
凯西·范·斯通

4

如果比较Bitmask枚举,将使用按位运算符。例如,您有一个状态枚举,并且一个对象可以处于多个状态中。在这种情况下,您将按位进行操作或为对象分配多个状态。

例如state = CONNECTED | IN_PROGRESS在哪里CONNECTED could be 0x00000001IN_PROGRESS 0x00000010

有关更多信息,请查阅flag枚举文档。


多亏了您,我才学到了以前不知道的按位运算符的应用!但是在此示例中,应用按位运算符。我正在寻找一段编写良好的代码,在其中使用按位而不是条件会导致编译,但输出不正确。也就是说,如果存在这样的代码段。
Deerasha

我认为Java中不会发生这种情况,因为我相信 或&运算符不能按位和或或仅执行逻辑|的值 要么 &。我不记得在Java中是否是这种情况,但是可以肯定地在C#MSDN中知道:“二进制|运算符是为整数类型和bool预定义的。对于整数类型,|将计算其操作数的按位或。 bool操作数,| |计算其操作数的逻辑或”
pwny 2011年

经过更多研究后,我发现|之间的唯一区别是 和|| Java中的布尔操作数的&&&&&&是短路行为,因此您描述的场景实际上是不可能的。
pwny 2011年

0

一个简单的错误示例:

int condA=1, condB=2;

if (condA!=0 && condB!=0) {
    // correct!
}
if ((condA & condB)!=0) {
    // never executed
}

这里有两个条件,两者都不为零;但按位运算&结果为零。


@Javier:谢谢您的回答,但我有些困惑。我正在使用Java,并且此代码无法编译。(condA && condB)错误,因为&&在2 ints内不起作用,因此只有2个布尔值。(condA & condB)虽然正确,但求值为int ,在Java中我们无法说出if(int)它也出错。您是第一个了解我在寻找什么的人-正是关于错误的例子
Deerasha

很久没有使用Java了...试试(Boolean(condA) && Boolean(condB)) (我认为Boolean(x)true非零整数,对吗?)
Javier

Nope @Javier:无法从int转换为boolean。
Deerasha

那又如何((condA!=0) && (condB!=0))呢?
哈维尔

@Javier是的,它可以编译,但是不再显示“错误”,将同时 if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }执行两个打印语句。
Deerasha 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.