&、|、^ 是按位运算符还是逻辑运算符?

Posted

技术标签:

【中文标题】&、|、^ 是按位运算符还是逻辑运算符?【英文标题】:Are the &, |, ^ bitwise operators or logical operators? 【发布时间】:2012-07-20 19:54:56 【问题描述】:

首先我了解到&|^ 是按位运算符,现在有人用&&|| 将它们称为逻辑运算符,我完全糊涂了——同一个运算符有两个名字?已经有逻辑运算符&&||,那为什么还要使用&|^

【问题讨论】:

另见:***.com/questions/11411907 【参考方案1】:

Java 类型字节是有符号的,这可能是位运算符的问题。当负字节扩展到 int 或 long 时,符号位被复制到所有高位以保留解释值。例如:

byte b1=(byte)0xFB; // that is -5
byte b2=2;
int i = b1 | b2<<8;
System.out.println((int)b1); // This prints -5
System.out.println(i);       // This prints -5

原因:(int)b1 内部是 0xFFFB 而 b2

解决方案:

int i = (b1 & 0xFF) | (b2<<8 & 0xFF00);
System.out.println(i); // This prints 763 which is 0x2FB

【讨论】:

【参考方案2】:

有语言参考是一回事,正确解释是另一回事。

我们需要正确解释事物。

即使 Java 记录了 &amp; 既是按位的又是逻辑的,我们可以提出一个论点,即 &amp; 从远古时代就确实没有失去其逻辑运算符的魔力,因为 C。也就是说,@987654324 @ 首先是一个本质上的逻辑运算符(尽管当时是非短路的)

&amp; 在词法+逻辑上解析为逻辑运算。

为了证明这一点,这两行的行为都是一样的,从 C 开始到现在(Java、C#、php 等)

if (a == 1 && b)

if (a == 1 & b)

也就是说,编译器会将这些解释为:

if ( (a == 1) && (b) )

if ( (a == 1) & (b) )

即使变量ab 都是整数。这……

if (a == 1 & b)

... 仍将被解释为:

if ( (a == 1) & (b) )

因此,这将对不利于整数/布尔对偶性的语言产生编译错误,例如Java 和 C#:

if (a == 1 & b)

事实上,在上面的编译错误上,我们甚至可以说&amp;没有失去它的逻辑(非短路)操作mojo,我们可以得出结论,Java延续了C制作的传统&amp; 仍然是一个逻辑操作。因此,我们可以说它是相反的,即&amp; 可以重新用作按位运算(通过应用括号):

if ( a == (1 & b) )

所以我们在另一个平行宇宙中,有人可能会问,如何使&amp; 表达式成为位掩码操作。

如何进行以下编译,我在 JLS 中读到 &amp; 是按位的 手术。 a 和 b 都是整数,但我不明白为什么 以下按位运算是 Java 中的编译错误:

如果 (a == 1 & b)

或者这样的问题:

为什么以下没有编译,我在 JLS 中读到 &amp; 是按位的 两个操作数都是整数时的运算。 a 和 b 都是 整数,但它让我无法理解为什么以下按位运算是 Java中的编译错误:

如果 (a == 1 & b)

事实上,如果已经存在类似于上述问题的现有 *** 问题,询问如何在 Java 中执行该掩码习语,我不会感到惊讶。

为了使语言的逻辑操作解释变成按位,我们必须这样做(在所有语言上,C、Java、C#、PHP 等):

if ( a == (1 & b) )

所以要回答这个问题,这不是因为 JLS 以这种方式定义事物,而是因为 Java(以及受 C 启发的其他语言)的 &amp; 运算符用于所有意图和目的仍然是一个逻辑运算符,它保留了 C 的语法和语义。 就是这样,从 C 开始,从远古时代开始,甚至在我出生之前。

事情不是偶然发生的,JLS 15.22 不是偶然发生的,它有着悠久的历史。

在另一个平行宇宙中,&amp;&amp; 没有被引入到语言中,我们仍将使用&amp; 进行逻辑运算,今天人们甚至可能会问一个问题:

是不是,我们可以使用逻辑运算符&amp;进行位运算?

&amp; 不关心它的操作数是否为整数,是否为布尔值。它仍然是一个逻辑运算符,一个没有短路的运算符。事实上,在 Java 中(甚至在 C 中)强制它成为 位运算符 的唯一方法是在它周围加上括号。即

if ( a == (1 & b) )

想一想,如果 &amp;&amp; 没有被引入 C 语言(以及任何复制其语法和语义的语言),现在任何人都可能会问:

如何使用&amp;进行位运算?

总结一下,首先Java&amp;本质上是一个逻辑运算符(一个非短路的),它不关心它的操作数,它会照常工作(应用逻辑运算) ) 即使两个操作数都是整数(例如掩码习语)。您只能通过应用括号来强制它成为按位运算。 Java 延续了 C 的传统

如果 Java 的 &amp; 的操作数(整数 1 和整数变量 b 在下面的示例代码中)都是整数,则它确实是按位运算,则应该编译:

 int b = 7;
 int a = 1;

 if (a == 1 & b) ...

【讨论】:

不了解历史的人注定只能引用language reference 我对此的回应与其他答案相同。我知道 Java 在 C / C++ 中有前身。但真正的原因是Java & 和|运营商之所以如此,是因为他们工作。事实上,由于它们没有借用 C/C++ 语义,它们实际上比 C/C++ 工作得更好……从某种意义上说,它们更简单、更易于理解且不那么脆弱。这也意味着根据 Java 运算符的历史来描述它们实际上并不能 1) 回答问题,或 2) 帮助理解。 (而且使用混淆的术语也无济于事。) 下面是一个反例,您断言 Java &amp;| 是“天生”合乎逻辑的。 a = b &amp; 42; 没有输入 ab 使得 &amp; 是一个逻辑运算符。很抱歉,您的论点根本站不住脚,将整个段落加粗并不会改变这一点。 @StephenC 我只是在逻辑操作的上下文中说它,例如关于 if 语句。在条件表达式内部时,操作数类型没有任何固有的东西会使&amp; 成为整数(例如屏蔽习语)或逻辑运算。 &amp; 总是在条件表达式中解析为逻辑运算。只有给操作数加上括号才能使其成为整数运算。不要让我断章取意,好吗? 1) 这个问题与if 中的运算符无关。它是关于 Java 中所有上下文中的运算符。 1a) 并且 JLS 没有在其术语中的上下文之间进行任意区分 2) 混淆的术语注释是指您与 JLS 所说的相矛盾的事实。这使得您的术语在 Java 上下文中是错误的和令人困惑的。 JLS 所说的是确定性【参考方案3】:

它们(&amp;|)很久以前被用于两个目的,逻辑运算符和位运算符。如果您要查看新生的 C(Java 语言的模式),&amp;| 被用作逻辑运算符。

但是由于在同一个语句中区分位运算和逻辑运算是非常混乱的,这促使丹尼斯·里奇为逻辑运算符创建了一个单独的运算符(&amp;&amp;||)。

在此处查看新生儿 C 部分:http://cm.bell-labs.com/who/dmr/chist.html

您仍然可以将位运算符用作逻辑运算符,其保留的运算符优先级就是证明。读出位运算符前世作为新生儿C上的逻辑运算符的历史

关于证据,我发表了一篇关于比较逻辑运算符和位运算符的博客文章。不言而喻,所谓的位运算符仍然是逻辑运算符,如果您尝试在实际程序中对比它们:http://www.anicehumble.com/2012/05/operator-precedence-101.html

我还在What is the point of the logical operators in C?上回答了与您的问题相关的问题

确实,位运算符也是逻辑运算符,尽管是短路逻辑运算符的非短路版本。


关于

已经有逻辑运算符&&、||,那为什么还要使用&、|、^?

XOR 很容易回答,就像一个单选按钮,只允许一个,下面的代码返回 false。为下面的人为代码示例道歉,认为同时喝啤酒和牛奶是不好的debunked already ;-)

String areYouDiabetic = "Yes";
String areYouEatingCarbohydrate = "Yes";


boolean isAllowed = areYouDiabetic == "Yes" ^ areYouEatingCarbohydrate == "Yes";

System.out.println("Allowed: " + isAllowed);

没有等效于 XOR 位运算符的短路,因为需要计算表达式的两边。

关于为什么需要使用&amp;| 位运算符作为逻辑运算符,坦率地说,您很难找到需要使用位运算符(也称为非短路逻辑运算符)作为逻辑运算符运营商。如果您想实现一些副作用并使您的代码紧凑(主观),那么逻辑操作可以是非短路的(通过使用按位运算符,也称为非短路逻辑运算符),例如:

while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) 

    // re-prompt


上面的代码可以改写成下面的样子(去掉括号),还是和前面代码的解释完全一样。

while ( !password.isValid() & attempts++ < MAX_ATTEMPTS ) 

    // re-prompt


删除括号,但它仍然产生与括号相同的解释,可以使&amp;逻辑运算符痕迹更加明显。冒着听起来多余的风险,但我必须强调,没有括号的表达式不会被解释为:

while ( ( !password.isValid() & attempts++ ) < MAX_ATTEMPTS ) 

    // re-prompt


综上所述,使用&amp;运算符(更通俗地称为仅按位运算符,但实际上是按位和逻辑(非短路))进行非短路逻辑运算以达到副作用是聪明(主观),但不被鼓励,这只是为了换取可读性的一种节省效应。

示例来源:Reason for the exsistance of non-short-circuit logical operators

【讨论】:

-1:这更多是关于 C 及其历史,而不是 Java 细节。 Java中的措辞有点不同,并没有这么多的C历史。 这个答案大多是不正确的,而且大多是题外话。 && 和 ||运算符是 John McCarthy 发明的,在 C 语言中它们不仅仅是为了减少混淆:它们与 & 和 | 做不同的事情。 @A.H.和 EJP 相同的原则适用,Java 继承了 C 的运算符及其优先级,Java 没有偏离它。同样的原则也适用。我只是提供一个背景说明为什么Java按位运算符也是逻辑运算符,它可以追溯到C @EJP 感谢您知道(麦卡锡运营商),我并没有声称&amp;&amp; 是发明的(开始时甚至被称为发明,这都是非常微不足道的,你必须以一种或另一种方式为短路运算符提出一个语言标记)作者 Dennis Ritchie,他将其引入语言中以减少混淆,重新发明是不明智的(考虑另一种短路标记 -电路操作员)***,不是吗? @MichaelBuen:Java 与 C 不同,它有一个独特的布尔类型。因此 pure logicalpure bitwise 之间的区别是 Java 中的情绪。【参考方案4】:

Java 运算符&amp;|^ 是按位运算符或逻辑运算符...取决于操作数的类型。如果操作数是整数,则运算符是按位的。如果它们是布尔值,则运算符是逻辑的。

这不仅仅是我这么说的。 JLS 也以这种方式描述这些运算符;见JLS 15.22。

(这就像+ 表示加法或字符串连接......取决于操作数的类型。或者就像“玫瑰”,表示花或淋浴附件。或“猫”表示任一毛茸茸的动物或 UNIX 命令。单词在不同的上下文中表示不同的东西。编程语言中使用的符号也是如此。)


已经有逻辑运算符&amp;&amp;||,为什么还要使用&amp;|^

在前两个的情况下,这是因为运算符在何时/是否对操作数进行评估方面具有不同的语义。在不同的情况下需要两种不同的语义;例如

    boolean res = str != null && str.isEmpty();

    boolean res = foo() & bar();  // ... if I >>need<< to call both methods.

^ 运算符没有等效的短路,因为它根本没有意义。

【讨论】:

@Crackers 在这里查看一个示例,按位运算符作为逻辑运算符,尽管没有短路:anicehumble.com/2012/05/operator-precedence-101.html 不要忘记阅读新生儿 C 的基本原理有cm.bell-labs.com/who/dmr/chist.html @Crackers 他已经回答过了。布尔 => 逻辑,整数 => 按位。

以上是关于&、|、^ 是按位运算符还是逻辑运算符?的主要内容,如果未能解决你的问题,请参考以下文章

a|=b 是什么意思

当按位运算符做同样的事情时,为啥要使用逻辑运算符?

&= 的含义

C 逻辑运算, 移位运算 , 取整 , 取模(取余)

&和&&的区别是什么

java基础2---按位& |常量变量