C & PHP: сохранение настроек в целое число с использованием побитовых операторов? - PullRequest
15 голосов
/ 05 мая 2010

Я не знаком с побитовыми операторами, но мне кажется, что раньше они хранили простые настройки.

Мне нужно передать несколько опций включения / выключения функции, и я хотел бы использоватьодно целое число для этого.Как я могу настроить и прочитать эти параметры?

Ответы [ 3 ]

33 голосов
/ 05 мая 2010

Вы уверены, что можете сделать это на 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
8 голосов
/ 05 мая 2010

Это работает почти одинаково на обоих языках, сравнение друг с другом:

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);

?>

Обратите внимание, вам не обязательно использовать константы, я просто использую их по привычке.Оба примера запустятся, я скомпилировал версию C через gcc -Wall flags.c -o flags.Измените flags в любом примере на что-либо кроме FLAG_TWO или FLAG_ALL и (к сожалению) вафель не будет.

В версии C вам не нужно пощекотать препроцессор, это может быть просто enum и т. д. - это упражнение для читателя.

2 голосов
/ 05 мая 2010

цитаты «Идея не очень хорошая, правда. Вам лучше передать несколько логических значений. Если вы хотите использовать побитовое значение, тогда

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) { // meets your criterion, as would a switch, case, but ultimately not suited for use with flags
    return $in === $match;
}

function matchAny($in, $match) { // meets original criterion with more lexical name however it returns true if any of the flags are true
    return $in |= $match;
}

если вы затем захотите расширить это, выполнив определенные действия, только если бит x, y, z включен, тогда вы можете использовать следующее

function matchHas($in, $match) { // more bitwise than === as allows you to conditionally branch upon specific bits being set
    return $in &= $match;
}

Я также думаю, что если вы делаете то, что было сделано в приведенной выше цитате, флаги могут быть не лучшей идеей, точные значения могут быть лучше, что дает преимущество, позволяя делать более осторожные действия. (0-255) для 8-битных более 8 различных флагов

Причина, по которой флаги работают так хорошо, в том, что в базе 2 «8» не содержит «4», а «2» не содержит «1».

 ________________________
 |8|4|2|1|Base 10 Value |
 ------------------------
 |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             |
 ------------------------
...