关于位操作符:一个清晰的,外行的解释,在C中 | and || 之间的区别。

A clear, layman's explanation of the difference between | and || in c#?

好吧,我已经读过很多次了,但是我还没有听到一种清晰、易懂(和令人难忘)的方法来学习以下两者之间的区别:

1
if (x | y)

1
if (x || y)

..在C的上下文中。有人能帮我了解这个基本事实吗,以及C具体地说,如何区别对待他们(因为他们似乎做了同样的事情)。如果一段给定的代码之间的差异是无关的,那么作为最佳实践,我应该默认哪个?


||是逻辑或运算符。请看这里。如果至少有一个操作数是真的,则计算为true。只能将它与布尔操作数一起使用;将它与整数操作数一起使用是错误的。

1
2
3
// Example
var one = true || bar();   // result is true; bar() is never called
var two = true | bar();    // result is true; bar() is always called

|是OR运算符。请看这里。如果应用于布尔类型,则如果至少有一个操作数为真,则计算结果为true。如果应用于整数类型,则计算为另一个数字。如果至少有一个操作数具有相应的位集,则此数的每个位都设置为1。

1
2
3
4
5
6
// Example
var a = 0x10;
var b = 0x01;
var c = a | b;     // 0x11 == 17
var d = a || b;    // Compile error; can't apply || to integers
var e = 0x11 == c; // True

对于布尔操作数,a || ba | b相同,只有一个例外,即如果a为真,则不计算b。因此,||被称为"短路"。

If the difference a given piece of code has between them is irrelevant, which should I default to as a best-practise?

如前所述,两者之间的差异并不无关紧要,因此这个问题在一定程度上是没有意义的。至于"最佳实践",没有一个:您只需使用正确的运算符。一般来说,人们喜欢||而不是|,因为你可以确定它不会产生不必要的副作用。


与布尔操作数一起使用时,|运算符与||一样是逻辑运算符,但区别在于||运算符进行短路评估,而|运算符不进行短路评估。

这意味着第二个操作数总是使用|运算符来计算,但使用||运算符时,只有在第一个操作数的计算结果为false时,才计算第二个操作数。

两个运算符的表达式结果总是相同的,但如果第二个操作数的计算导致其他内容发生更改,则只有使用|运算符才能保证发生这种更改。

例子:

1
2
3
4
5
6
7
8
9
10
int a = 0;
int b = 0;

bool x = (a == 0 || ++b != 0);

// here b is still 0, as the"++b != 0" operand was not evaluated

bool y = (a == 0 | ++b != 0);

// here b is 1, as the"++b != 0" operand was evaluated.

||运算符的短路计算可用于编写较短的代码,因为只有在第一个操作数为真时才计算第二个操作数。不是这样写:

1
2
3
4
5
6
7
8
9
if (str == null) {
   Console.WriteLine("String has to be at least three characters.");
} else {
   if (str.Length < 3) {
      Console.WriteLine("String has to be at least three characters.");
   } else{
      Console.WriteLine(str);
   }
}

你可以这样写:

1
2
3
4
5
if (str == null || str.Length < 3) {
   Console.WriteLine("String has to be at least three characters.");
} else{
   Console.WriteLine(str);
}

第二个操作数只有在第一个操作数为false时才进行计算,因此您知道可以安全地在第二个操作数中使用字符串引用,因为如果计算第二个操作数,则它不能为空。

在大多数情况下,您希望使用||运算符,而不是|运算符。如果第一个操作数为假,则无需计算第二个操作数以获得结果。而且,很多人(显然)不知道可以将|运算符与布尔操作数一起使用,因此当在代码中看到它以这种方式使用时,他们会感到困惑。


它们不一样。一个是位"或",一个是逻辑"或"。

x y是逻辑或,表示与"x或y"相同,适用于bool值。它用于条件或测试中。在这种情况下,x和y可以替换为任何计算为bool的表达式。例子:

1
if (File.Exists("List.txt")  ||  x > y )  { ..}

如果这两个条件之一为真,则子句的计算结果为真。如果第一个条件为真(如果文件存在),则第二个条件不需要也不会被计算。

单管()是按位或。要知道这意味着什么,你必须了解数字是如何存储在计算机中的。假设您有一个16位的数量(int16),它保存值15。它实际存储为0x000F(十六进制),与0000 0000 0000 1111(二进制)相同。按位"或"将两个量和"或"的每对对应的位放在一起,这样,如果两个量中的一个位为1,则结果为1。因此,如果a=0101 0101 0101(十六进制为0x5555),b=1010 1010 1010(十六进制为0xAAAA),则a b=1111 1111 1111 1111=0xffff。

您可以使用C中的位或(单管)来测试是否打开了一组特定的位中的一个或多个。如果要测试12个布尔值或二进制值,并且它们都是独立的,那么可以这样做。假设您有一个学生数据库。一组独立的布尔值可能是这样的:男性/女性、家庭/校园内、当前/非当前、已注册/未注册等。如果不为这些值中的每一个存储布尔字段,则可以为每个值存储一个位。男性/女性可能是1号钻头。已注册/未注册可能是位2。

然后你可以用

1
 if ((bitfield | 0x0001) == 0x0001) { ... }

作为测试,看是否没有位被打开,除了"学生是男性"位,这是被忽略的。呵呵?好吧,按位或返回一个1,表示在任何一个数中打开的每个位。如果位或以上的结果=0x001,这意味着位字段中没有打开的位,除了第一个位(0x001),但您不能确定第一个位是否打开,因为它被屏蔽了。

有一个对应的&;&;and&;,它是逻辑的和位的和。他们有类似的行为。

你可以使用

1
 if ((bitfield &  0x0001) == 0x0001) { ... }

查看是否在位字段中打开了第一个位。

编辑:我真不敢相信我被投票否决了!


不像大多数答案,到目前为止,意思是不完全一样,在C++中。

对于计算布尔值的任何两个表达式a和b,a b和a b执行几乎相同的操作。

A B同时计算A和B,如果其中一个计算为真,则结果为真。

A B做了几乎相同的事情,只是它先计算A,然后在必要时才计算B。如果a或b为真,则整个表达式都为真,如果a为真,则根本不需要测试b。因此短路,并在可能的情况下跳过对第二个操作数的计算,其中运算符将始终对这两个操作数进行计算。

运算符不经常使用,而且通常不会产生影响。我能想到的唯一一个不同之处是:

1
2
3
if ( foo != null || foo.DoStuff()){ // assuming DoStuff() returns a bool

}

这是因为如果左测试失败,则从不调用dostuff()成员函数。也就是说,如果foo为空,我们就不会在其上调用dostuff。(这将给我们一个nullreferenceexception)。

如果我们使用了运算符,那么无论foo是否为空,都将调用dostuf()。

在整数上,只有运算符被定义,并且是位"或",正如其他答案所描述的那样。但是运算符没有为整数类型定义,因此很难将它们混合到C中。


好的答案,但我要补充一点,如果左侧表达式是true,则不会计算||的右侧表达式。在评估术语为a)绩效密集型或b)产生副作用(罕见)的情况下,请记住这一点。


|是按位或运算符(数字、整数)。它的工作原理是将数字转换成二进制,然后对每个对应的数字执行或。同样,数字在计算机中已经用二进制表示,因此在运行时不会真正发生这种转换;)

||是逻辑或运算符(布尔值)。它只适用于真值和假值。


下面将在C/C++中工作,因为它没有布尔类的第一类支持,它将每个表达式的"on"位视为真,否则为false。事实上,如果X和Y是数字类型,下面的代码将不能在C或Java中工作。

1
if (x | y)

所以上面代码的显式版本是:

1
if ( (x | y) != 0)

在C语言中,任何在其上有"on"位的表达式都会得到true。

int i=8;

如果(i)//在c中有效,则结果为真

IN-JOY=- 10;

如果(joy)//在c中无效,则结果为真

现在回到C

如果x和y是数字类型,则代码:if(x_y)将不起作用。你试过编译它吗?它不起作用

但是对于您的代码,我可以假定x和y是布尔类型,所以它会起作用,所以对于布尔类型,和之间的区别是,是短路的,而不是。以下输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    static void Main()
    {


        if (x | y)
            Console.WriteLine("Get");

        Console.WriteLine("Yes");

        if (x || y)
            Console.WriteLine("Back");

        Console.ReadLine();
    }


    static bool x
    {
        get { Console.Write("Hey");  return true; }
    }

    static bool y
    {
        get { Console.Write("Jude"); return false; }
    }

是:

1
2
3
HeyJudeGet
Yes
HeyBack

Jude不会被打印两次,其中是一个布尔运算符,许多C派生语言布尔运算符短路,如果布尔表达式短路,则布尔表达式的性能更高。

对于外行术语,当您说短路时,例如在(或运算符)中,如果第一个表达式已经为真,则不需要计算第二个表达式。示例:如果(answer='y'answer='y'),如果用户按small y,则程序不需要计算第二个表达式(answer='y')。那是短路。

在我上面的示例代码中,x是真的,因此y on运算符不会被进一步评估,因此没有第二个"jude"输出。

即使x和y是布尔类型,也不要在c中使用这种代码:if(x y)。不表演。


强烈建议从dotnet mob阅读本文

For OR logical operation if any of it's operand is evaluated to true then
whole expression is evaluated to true

这就是操作符所做的-当它找到一个真值时,它跳过剩余的评估。虽然运算符计算它的完整操作数以评估整个表达式的值。

1
2
3
4
5
6
7
8
if(true||Condition1())//it skip Condition1()'s evaluation
{
//code inside will be executed
}
if(true|Condition1())//evaluates Condition1(), but actually no need for that
{
//code inside will be executed
}

无论是或()或和(&;&;)运算符,最好使用短路版本的逻辑运算符。


考虑以下代码段

1
2
3
4
5
6
7
int i=0;
if(false||(++i<10))//Now i=1
{
//Some Operations
}
if(true||(++i<10))//i remains same, ie 1
{}

这种效果称为副作用,实际上在表达式的右侧用短路逻辑运算符表示。

参考:C中的短路评估#


在不以任何方式、形状或形式钻研细节的情况下,这里有一个真正的外行版本。

把英语中的""想象成一个直线或",把英语中的""想象成"或"其他"。

同样,在英语中也可以将"&;as"和"视为";在英语中也可以将"&;&;as"和"视为"。

如果你用这些术语给自己读一个表达,它们通常会更有意义。


第一个位运算符处理两个数值,得到第三个数值。

如果你有二进制变量

1
2
a = 0001001b;
b = 1000010b;

然后

1
a | b == 1001011b;

也就是说,如果结果中的某个位在任一操作数中也是1,则该位就是1。(为了清晰起见,我的示例使用8位数字)

"double pipe",是一个逻辑或运算符,它接受两个布尔值并产生第三个值。


虽然这句话已经被正确地说和回答了,我想我会添加一个真正的外行的答案,因为很多时候,这是我在这个网站上的感觉:)。另外,我将添加&vs.&;的示例,因为它是相同的概念

V.V.*

基本上,如果第一个部分是错误的,那么您倾向于使用来评估第二个部分。因此:

1
if (func1() || func2()) {func3();}

是一样的

1
2
3
4
5
6
7
8
if (func1())
{
    func3();
}
else
{
    if (func2()) {func3();}
}

这可能是节省处理时间的一种方法。如果处理func2()花费了很长时间,那么如果func1()已经为真,则不希望执行此操作。

&与

在&vs.&;的情况下,类似的情况是,如果第一部分是真的,您只评估第二部分。例如:

1
if (func1() && func2()) {func3();}

是一样的

1
2
3
4
if (func1())
{
    if (func2()) {func3();}}
}

这是必需的,因为func2()可能首先依赖于func1()为真。如果使用的&;和func1()的计算结果为false,则&;将以任何方式运行func2(),这可能导致运行时错误。

门外汉杰夫