16

我不熟悉按位运算符,但我之前似乎使用它们来存储简单的设置。

我需要将几个开/关选项传递给一个函数,我想为此使用一个整数。我该如何设置和阅读这些选项?

4

3 回答 3

37

你肯定可以在 PHP 中做到这一点。

假设您有四个布尔值要存储在一个值中。这意味着我们需要四位存储空间

0000

每个位在单独设置时都有唯一的十进制表示

0001 = 1 // or 2^0
0010 = 2 // or 2^1
0100 = 4 // or 2^2
1000 = 8 // or 2^3

实现这一点的常用方法是使用位掩码来表示每个选项。例如,PHP 的错误级别就是以这种方式完成的。

define( 'OPT_1', 1 );
define( 'OPT_2', 2 );
define( 'OPT_3', 4 );
define( 'OPT_4', 8 );

然后,当您有一个表示 0 个或更多这些标志的整数时,您可以使用按位和运算符进行检查,即&

$options = bindec( '0101' );
// can also be set like this
// $options = OPT_1 | OPT_3;

if ( $options & OPT_3 )
{
  // option 3 is enabled
}

该运算符的工作原理是:只有在两个操作数中设置的位才会在结果中设置

0101 // our options
0100 // the value of OPT_3
----
0100 // The decimal integer 4 evaluates as "true" in an expression

如果我们检查它OPT_2,那么结果将如下所示

0101 // our options
0010 // the value of OPT_2
----
0000 // The decimal integer 0 evaluates as "false" in an expression
于 2010-05-05T14:30:43.177 回答
8

它在两种语言中的工作方式几乎相同,并排比较:

C:

#include <stdio.h>
#include <stdint.h>

#define FLAG_ONE 0x0001
#define FLAG_TWO 0x0002
#define FLAG_THREE 0x0004
#define FLAG_FOUR 0x0008
#define FLAG_ALL (FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR)

void make_waffles(void)
{
   printf("Yummy! We Love Waffles!!!\n");
}

void do_something(uint32_t flags)
{
    if (flags & FLAG_TWO)
         make_waffles();
}

int main(void)
{
    uint32_t flags;

    flags |= FLAG_ALL;

    /* Lets make some waffles! */
    do_something(flags);

    return 0;
}

PHP:

<?php

define("FLAG_ONE", 0x0001);
define("FLAG_TWO", 0x0002);
define("FLAG_THREE", 0x0004);
define("FLAG_FOUR", 0x0008);
define("FLAG_ALL", FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR);

function make_waffles()
{
    echo 'Yummy! We Love Waffles!!!';
}

function do_something($flags)
{
    if ($flags & FLAG_TWO)
       make_waffles();
}

$flags |= FLAG_TWO;
do_something($flags);

?>

请注意,您不一定需要使用常量,我只是出于习惯使用它们。这两个示例都将运行,我通过gcc -Wall flags.c -o flags. 将任一示例更改flags为除FLAG_TWO或之外的任何内容,FLAG_ALL并且(可悲地)不会制作华夫饼。

在 C 版本中,您不必为预处理器发痒,它可以很容易地成为一个枚举,等等——这对读者来说是一个练习。

于 2010-05-05T14:33:13.220 回答
2

引用“这个想法不好,真的。你最好传递几个布尔值。如果你想按位使用,那么

function someFunc($options)
{

   if ($options & 1 != 0)
      //then option 1 enabled
   if ($options & (1 << 1) != 0)
      //then option 2 enabled      
   if ($options & (1 << 2) != 0)
      //then option 3 enabled      
}

"

如果您正在检查单个值,您所做的就可以了,虽然不是最佳的,所以检查一个位是否启用,但是假设我们希望能够匹配任何值,或者确切地说我们可以有以下方法

function matchExact($in, $match) { // 符合您的标准,就像 switch、case 一样,但最终不适合与标志一起使用
    返回 $in === $match;
}

function matchAny($in, $match) { // 符合原始标准,具有更多的词法名称,但是如果任何标志为真,则返回真
    返回 $in |= $match;
}

如果您随后想通过仅在启用位 x,y,z 时才发生特定操作来扩展此功能,则可以使用以下内容

function matchHas($in, $match) { // 比 === 更按位,因为允许您根据设置的特定位有条件地分支
    返回 $in &= $match;
}

我还认为,如果您正在执行上述引用中所做的事情,则标志可能不是最好的主意,确切的值可能会更好,这确实有利于采取更谨慎的行动。(0-255) 8 位超过 8 个不同的标志

标志工作如此出色的全部原因是因为在基数 2 中,“8”不包含“4”,而“2”不包含“1”。

________________________
 |8|4|2|1|基数为 10 的值 |
 ----------------------
 |1|1|1|1|15 |
 |1|1|1|0|14 |
 |1|1|0|1|13 |
 |1|1|0|0|12 |
 |1|0|1|1|11 |
 |1|0|1|0|10 |
 |1|0|0|1|9 |
 |1|0|0|0|8 |
 |0|1|1|1|7 |
 |0|1|1|0|6 |
 |0|1|0|1|5 |
 |0|1|0|0|4 |
 |0|0|1|1|3 |
 |0|0|1|0|2 |
 |0|0|0|1|1 |
 |0|0|0|0|0 |
 ----------------------
于 2010-05-05T14:18:29.443 回答