C & PHP:如何使用位运算符将设置存储在整数中?

16

我对位运算符并不熟悉,但我见过它们用于存储简单的设置。

我需要将多个开关选项传递给一个函数,并且希望只使用一个整数来实现。我该如何设置和读取这些选项?


我认为这不是一个完整的答案,所以我会将它作为评论放置,但请看一下UNIX上文件和目录的权限作为例子。 - Clutch
1
请参考以下链接,了解PHP中的位运算:https://dev59.com/bnI95IYBdhLWcg3w1Bro#2131828 - Gordon
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

1
谢谢你把所有东西都讲解得如此清楚明白。 - John Coates

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编译了C版本。在任何一个示例中将flags更改为除FLAG_TWOFLAG_ALL之外的任何内容,(遗憾地)将不会制作华夫饼。
在C版本中,你不需要触动预处理器,它可以很容易地成为一个枚举类型等——这是读者的练习。

很好,这个例子非常容易理解。我一直在尝试理解位运算符。感谢您提供PHP和C两种编程语言的代码,因为我会同时使用它们。 - John Coates
它们都是非常好的答案。我尝试接受两个,但不起作用。 - John Coates
@Andrew M - 我已经修复了C语言示例,使其与PHP示例相同,并确保您可以编译和运行它。 - Tim Post

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,但最终不适用于标志 return $in === $match; }
function matchAny($in, $match) { // 满足原始标准,具有更多词汇名称,但如果任何标志为真,则返回true return $in |= $match; }
如果您想通过仅在启用位x、y、z时发生特定操作来扩展此功能,则可以使用以下内容:
function matchHas($in, $match) { // 更加位运算,因为它允许您有条件地分支特定位被设置 return $in &= $match; }
我认为,如果您正在执行上述引号中的操作,则标志可能不是最好的选择,确切的值可能更好,这样可以使操作更加离散。(0-255) 用于8个不同的标志
标志之所以如此有效,是因为在二进制中,“8”不包含“4”,“2”不包含“1”。

@Kendall Hopkins,“显式优于隐式”,这正如《Python之禅》所说的一样。;) - Andrey

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