Why '&&' and not '&'?
为什么
我问了一个编程多年的人,他的解释是:
例如,在
注意:我想指出的是,我的编程相当于一个刚学走路的孩子,这不是一个严重或紧急的问题。更重要的是理解为什么事情应该以某种方式而不是另一种方式进行。
在大多数情况下,
例子:
1 2 3 | if(CanExecute() && CanSave()) { } |
如果
这在以下情况下非常方便:
1 2 3 4 5 | string value; if(dict.TryGetValue(key, out value) && value.Contains("test")) { // Do Something } |
如果在字典中找不到提供的密钥,
类似但更简单的例子是以下代码(如Tjeuvel所提到的):
1 2 3 4 | if(op != null && op.CanExecute()) { // Do Something } |
只有当
除此之外,从技术上讲,它们也是不同的:
更确切地说,在c中,这些运算符(
非常清楚地解释这意味着什么(即使其他答案也暗示了这一点——但可能使用了你不理解的术语)。
以下代码:
1 2 3 4 | if (a && b) { Foo(); } |
是这样的:
1 2 3 4 5 6 7 | if (a) { if (b) { Foo(); } } |
其中,以下代码的编译与所表示的完全相同:
1 2 3 4 | if (a & b) { Foo(); } |
这叫做短路。一般来说,在您的条件下,您应该始终使用
加分:有一种情况是你不应该这样做的。如果你处在一个性能至关重要(这是纳秒的关键)的情况下,只在你必须(例如
批判/独白:关于分支错误预测,最幸福地忽略。引用安迪·费斯(他在游戏方面工作了13年)的话:"这可能是人们认为他们需要去的较低水平……但他们错了。了解正在为treats分支编程的硬件如何在很大程度上影响性能…比大多数程序员可能更欣赏的是Re:Deather by a Celly Cuts。"
- 游戏开发人员(以及其他在极端实时条件下工作的人员)尽可能地重组他们的逻辑,以更好地适应预测器。在反编译的mscorlib代码中也有这方面的证据。
- 仅仅因为.NET保护你不受这类事情的影响并不意味着它不重要。分支错误预测在60赫兹或每秒10000个请求时非常昂贵。
- 英特尔没有工具来识别错误预测的位置,Windows也没有性能计数器,也没有词来描述它,如果这不是一个问题的话。
- 对较低层次和体系结构的无知不会使人意识到它们是错误的。
- 始终尝试理解您正在工作的硬件的局限性。
这里是非信徒的基准。最好实时/高速运行该进程,以减轻调度程序的影响:https://gist.github.com/1200737
逻辑运算符(
逻辑运算符和位运算符之间最关键的区别是,逻辑运算符接受两个布尔值并生成一个布尔值,而位运算符接受两个整数并生成一个整数(注意:整数表示任何整数数据类型,而不仅仅是int)。
为了学究,位运算符采用位模式(例如01110111),并对每个位执行位和/或。例如,如果有两个8位整数:
1 2 3 4 5 | a = 00110010 (in decimal: 32+16+2 = 50) b = 01010011 (in decimal: 64+ 16+2+1 = 83) ---------------- a & b = 00010010 (in decimal: 16+2 = 18) a | b = 01110011 (in decimal: 64+32+16+2+1 = 115) |
虽然逻辑运算符只在
1 2 3 4 5 | a = true b = false -------------- a && b = false a || b = true |
其次,通常可以对bool使用位运算符,因为true和false分别等于1和0,如果将true转换为1,将false转换为0,则执行位运算,然后将非零转换为true,将零转换为false;如果仅使用逻辑运算符,则结果将相同(选中t他的运动)。
另一个重要区别是逻辑运算符短路。因此,在某些圈子[1]中,你经常看到人们这样做:
1 2 3 | if (person && person.punch()) { person.doVictoryDance() } |
也就是说:"如果人存在(即不为空),试着打他/她,如果打成功(即返回真),那么就跳一支胜利之舞。"
如果使用位运算符,则:
1 2 3 | if (person & person.punch()) { person.doVictoryDance() } |
将翻译为:"如果人存在(即不为空),并且击打成功(即返回真值),那么就跳一支胜利之舞。"
注意,在短路逻辑运算符中,如果
[1]有些程序员会拒绝将具有副作用的函数调用放入
由于位运算符一次只能处理32位(如果您在32位计算机上),因此如果需要比较大量的条件(例如,如果您需要比较多个条件),它可能会导致代码更优雅、更快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3, CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6; Person person; person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS; Place bar; bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK; Place military; military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT; CurrentLocation cloc1, cloc2; cloc1.usable_abilities = person_abilities & bar_rules; cloc2.usable_abilities = person_abilities & military_rules; // cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT` // while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT` |
对逻辑运算符执行相同操作需要进行大量的比较:
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 26 27 28 | Person person; person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true; person.can_shoot_cannons = false; Place bar; bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true; bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false; Place military; military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true; military.rules.can_drink = military.rules.can_talk = false; CurrentLocation cloc1; bool cloc1.usable_abilities.can_punch = bar.rules.can_punch && person.can_punch, cloc1.usable_abilities.can_kick = bar.rules.can_kick && person.can_kick, cloc1.usable_abilities.can_drink = bar.rules.can_drink && person.can_drink, cloc1.usable_abilities.can_sit = bar.rules.can_sit && person.can_sit, cloc1.usable_abilities.can_shoot_guns = bar.rules.can_shoot_guns && person.can_shoot_guns, cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons cloc1.usable_abilities.can_talk = bar.rules.can_talk && person.can_talk; bool cloc2.usable_abilities.can_punch = military.rules.can_punch && person.can_punch, cloc2.usable_abilities.can_kick = military.rules.can_kick && person.can_kick, cloc2.usable_abilities.can_drink = military.rules.can_drink && person.can_drink, cloc2.usable_abilities.can_sit = military.rules.can_sit && person.can_sit, cloc2.usable_abilities.can_shoot_guns = military.rules.can_shoot_guns && person.can_shoot_guns, cloc2.usable_abilities.can_talk = military.rules.can_talk && person.can_talk, cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons; |
UNIX/Linux文件系统权限中使用位模式和位运算符的经典示例。
在以下情况下:
1 | if (obj != null && obj.Property == true) { } |
会按预期工作。
但是:
1 | if (obj != null & obj.Property == true) { } |
可能引发空引用异常。
简明扼要:
但是
0.001和0.01的位,以给出0.000=0的十进制数。
同样,对于和操作员也一样…!
在逻辑表达式(如if语句
但是,如果表达式being或ed或ed加在一起有副作用,并且您希望所有这些都是表达式的结果(不管逻辑表达式的结果如何),那么可以使用
1 2 3 4 5 6 | int a = 0; // 0 means all bits off a = a | 4; // set a to binary 100 if ((a & 4) != 0) { // will do something } a = a & (~4) // turn bit off again, a is now 000 |
在C以外的语言中,必须注意&;和的逻辑和位模式。在上面的代码中,
这可能会导致混淆,甚至在使用位和运算符组合的表达式返回没有对齐位的值时可能会出现问题。在检查两个函数是否都成功(由它们返回非零值定义)之前,请考虑以下需要两个函数副作用的示例:
1 2 3 | if (foo() & bar()) { // do something } |
在C语言中,如果
c要求像
1 2 3 | if (foo() != 0 & bar() != 0) { // do something } |
如果你是一个老的定时器C程序员,小心点。C真的让我吃惊。
对于
Binary | operators are predefined for the integral types and bool. For integral types, | computes the bitwise OR of its operands. For bool operands, | computes the logical OR of its operands; that is, the result is false if and only if both its operands are false.
(重点是我的。)布尔类型是专门处理的,在这种情况下,问题才开始有意义,不同之处在于,其他类型的问题已经在其答案中进行了解释:
&& and|| are short-circuiting.& and| evaluate both operands.
最好的选择取决于很多方面,比如副作用、性能和代码可读性,但一般来说,短路运算符更可取,因为它们更容易被我这样背景相似的人理解。
原因是:我想这样论证:因为C中没有真正的布尔类型,所以可以使用按位运算符
1 | if (list.Count() > 14 && list[14] =="foo") |
是安全的
1 | if (list.Count() > 14 & list[14] =="foo") |
如果列表大小不正确,将崩溃。
如果我们正在评估
C操作员应解释原因:
本质上有两个
&;operator有一个使用一个
好的,按面值计算
1 2 3 4 5 6 7 8 9 10 | Boolean a = true; Boolean b = false; Console.WriteLine("a({0}) && b({1}) = {2}", a, b, a && b); Console.WriteLine("a({0}) || b({1}) = {2}", a, b, a || b); Console.WriteLine("a({0}) == b({1}) = {2}", a, b, a == b); Console.WriteLine("a({0}) & b({1}) = {2}", a, b, a & b); Console.WriteLine("a({0}) | b({1}) = {2}", a, b, a | b); Console.WriteLine("a({0}) = b({1}) = {2}", a, b, a = b); |
给出相同的答案。但是,正如您所展示的,如果您有一个更复杂的问题,那么:
1 | if (a and b and c and d) .. |
如果
我一直使用
1 2 | if (limit && !MyDictionary.ContainsKey("name")) continue; |
如果不是
&&;和&;意味着两件截然不同的事情,并给出两个不同的答案。
因为
1 | a() && b() && c() && d(); |
甚至
1 | w() || x() || y() || z(); |
它不仅比同等的
向不需要知道代码的精确操作的人解释这一点的最快(并且稍微变小)的方法是
&正在对这些条件中的每个条件进行检查,直到发现错误并将整个结果返回为错误为止。
||正在对这些条件中的每一个进行检查,直到找到一个true并将整个结果返回为true。
&正在进行基于数学的apon,包括/所有条件和处理结果。
|正在做基于数学的apon,包括所有条件和处理结果。
我从来没有遇到过需要在if语句中使用&;或的情况。我主要使用它通过按位移位将十六进制值分割成它的组分颜色。
如:
1 2 3 | r = fullvalue >> 0xFF & 0xFF; g = fullvalue >> 0xF & 0xFF; b = fullvalue & 0xFF; |
在此操作中,"0xff"将强制只查看二进制值。不过,我个人还没有找到一种用途。
简单地说,
如果exp1是
但是
如果exp1是
而且很少有人使用
这很重要,因为如果bool2(例如)的评估成本很高,但bool1是错误的,那么通过使用&;&;over&;可以为自己节省一点计算成本。