理解PHP中的&(和号,位与)运算符

68

我在我的代码中经常使用($var & 1),如果$var是奇数则返回true,如果是偶数则返回false。

但是"&"到底是什么意思呢?


6
如果您能在 Stack Overflow 上找到任何编程问题的答案,那不是很好吗?这只有在“简单”的问题和“难题”问题都被问到的情况下才可能发生。基于这一点,我认为这是一个有效的问题。 - da5id
10
不要低估那些成功的程序员中不懂位运算的人数。这是一个合理的问题,全面的了解有助于解释电路、加密和哈希等底层工作原理。 - thomasrutter
1
一个人如果不知道在线手册在哪里,就无法编写 PHP 代码。而“运算符”这一节显然是必须掌握的。 - dkretz
16
虽然手册中可能有提到,但我当时找不到,现在依然找不到。在向 Stack Overflow 提问之前,我会尽量认真地去做功课。如果你认为问题没有必要,为什么要给我难堪?回答、投反对票或者直接跳过即可。 - ryonlife
11
我在这里获得的答案质量(包括对二进制的详细解释)比我在谷歌、PHP文档和SO上自己研究15分钟所能找到的要好得多。这正是这个社区成为如此宝贵的资源的原因。 - ryonlife
显示剩余5条评论
5个回答

64

& 是二进制的 and 运算符。如果您有一个二进制值,并且使用另一个二进制值进行 and 运算,则结果将是两个值的按位 and 运算结果。例如:

  01101010
& 01011001
= 01001000

最右边的二进制位是1时(这种情况下该数字为奇数),或者它是0时(此时该数字为偶数)。如果您将一个数字与1 & 运算,则只查看最低有效位,然后检查该数字是否为1或0。正如其他人所提到的,查看按位运算符以获取有关它们工作方式的信息。


你能给出一些有用的例子,说明它实际上可以用来做什么吗?因为奇数或偶数也可以使用模运算完成,对吧。而这种方式更容易理解和阅读,比如 $is_even = $x % 2 === 0; $is_odd = !$is_even; - Toskan

30

二进制系统中的两个基本运算是 OR 和 AND。

OR 意味着“如果 A 或 B 任意一个为 1(表示开),则输出 1”。一个现实世界的例子是平行接通的两个电灯开关。只要其中任何一个开关打开,电流就可以通过。

AND 意味着“如果 A 和 B 都为 1,则输出 1”。实际世界中的例子是串接的两个电灯开关。只有当两个开关都打开时,电流才能通过。

在计算机中,这些不是物理开关,而是半导体,它们的功能被称为 逻辑门。它们执行与开关相同类型的操作 - 响应电流或无电流。

当应用于整数时,每个数字中的每个位都会与另一个数字中的每个位相结合。因此,要理解按位运算符 OR 和 AND,您需要将数字转换为二进制,然后对每对匹配的位执行 OR 或 AND 运算。

这就是为什么:

00011011 (odd number)
AND
00000001 (& 1)
== 
00000001 (results in 1)

尽管

00011010 (even number)
AND
00000001 (& 1)
==
00000000 (results in 0)
因此,(& 1) 操作使用AND逻辑将最右边的位与1进行比较。其他所有位都被忽略,因为任何东西与无所谓是无关紧要的。在二进制中,偶数也是十进制中的偶数(10是2的倍数)。
其他基本的二进制操作包括NOT和XOR。NOT表示“如果A关闭”,它是仅采用一个信号或“参数”的逻辑门的形式。XOR表示“如果A或B打开,但不是两者都打开”。然后还有NAND、NOR和NXOR,它们基本上只是NOT与AND、OR和XOR结合而成的,例如,NAND表示“如果A和B都不打开”。
在编程中,该操作符
& means AND,
| means OR, 
~ means NOT, and
^ means XOR.
其他的可以通过组合这些来完成,例如:
~ (a & b) is equivalent to a NAND operation

PHP特别提示

按位运算符不适用于浮点数,在PHP中,浮点数将隐式转换为整数。表示为整数的范围之外的数字将截断为零 - 也就是说,所有大于PHP_INT_MAX的数字在表达式 ($num & 1) 中看起来都是“偶数”。如果您想支持PHP_INT_MIN / PHP_INT_MAX之外的数字,则需要使用fmod($num, 2)。但是,如果您使用的是64位PHP,则整数的精度比浮点数更高。


4
我认为这比被接受的答案更好地解释了该问题,因为它实际上解释了位运算在幕后的操作,这对于理解和使用它们是关键的。 - Byson

19

这对于了解PHP和按位运算也很有趣:

/**
 * Regular
 */
echo (true && true); // 1
echo (true && false); // nothing

echo (true || false); // 1
echo (false || false); // nothing

echo (true xor false); // 1
echo (false xor false); // nothing

/**
 * Bitwise
 */
echo (true & true); // 1
echo (true & false); // 0

echo (true | false); // 1
echo (false | false); // 0

echo (true ^ false); // 1
echo (false ^ false); // 0

1
在我看来,这个答案并没有很好地解释&&和&之间的区别。&&将两个参数都转换为布尔值,并且像比较单个位一样。&以按位方式进行比较:一个参数中的每个位都与另一个参数中的每个位进行比较。 - thomasrutter
3
“regular”被称为逻辑“与/或”。 - Ryan Kearney
2
同样需要注意的是,位运算符(如&和|)是用于整数的。本答案中的示例之所以有效,是因为“true”转换为“1”,而“false”转换为“0”。如果您使用echo ("cheese" & 4),它将输出零,而echo ("cheese" && 4)将输出true。 - thomasrutter

15

我知道你的问题是关于理解位运算符的,已经有一个很好的答案解释了它。但是针对你给出的例子,我不能不推荐你使用取模运算符:

($var % 2) /* instead of */ ($var & 1)

因为这样可以清晰地表明你正在检查数字是否是奇数(不可被2整除),而且它更加通用,所以你可以像使用($var % 3)一样使用它,并推导出它适用于任何N的工作原理。


12

除了其他答案之外,值得注意的是

if(func1() && func2())

仅在func1()返回true时调用func2()("惰性求值"),而

if(func1() & func2())
无论如何都会调用这两个函数,但是前提是它们返回布尔值时的真值表将相同。


thomasrutter指出(在下面的评论中)实践中可能不应该做后者。(A & B)(A && B)在真值上可能并不相同,尤其当AB是整数时。例如,如果A=1且B=2(都为真),则A&B将为假,而A&&B为真。此外,另一个开发人员可能会认为这是笔误并“更正”为两个和号。


1
虽然这个方法“大多数情况下”有效,但它并不总是有效,例如当函数返回数字时。假设func1()返回1,而func2()返回4。 func1()&& func2()将评估为“true”,而func1()& func2()将评估为“0”。即使在它有效的情况下,我也不会称它为良好的编码实践,因为它依赖于评估顺序和类型转换的副作用,使代码更难以阅读和理解其目的。任何审查代码的人都可能认为这是一个拼写错误。 - thomasrutter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接