C / C ++ эффективный битовый массив - PullRequest
32 голосов
/ 14 апреля 2010

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

Сейчас я использую обычную битовую маску int / char, но они не очень чистые, когда длина массива больше длины типа данных.

std vector<bool> недоступно для меня.

Ответы [ 9 ]

49 голосов
/ 14 апреля 2010

Поскольку вы упоминаете как C, так и C ++, я предполагаю, что ориентированное на C ++ решение, такое как boost::dynamic_bitset, может быть неприменимо, и вместо этого расскажу о низкоуровневой реализации C. Обратите внимание, что если что-то вроде boost::dynamic_bitset работает для вас, или вы можете найти уже существующую библиотеку C, то использовать их может быть лучше, чем развернуть вашу собственную.

Предупреждение : Ни один из следующего кода не был протестирован или даже скомпилирован, но он должен быть очень близок к тому, что вам нужно.

Для начала предположим, что у вас фиксированный размер набора битов N. Тогда работает что-то вроде следующего:

typedef uint32_t word_t;
enum { WORD_SIZE = sizeof(word_t) * 8 };

word_t data[N / 32 + 1];

inline int bindex(int b) { return b / WORD_SIZE; }
inline int boffset(int b) { return b % WORD_SIZE; }

void set_bit(int b) { 
    data[bindex(b)] |= 1 << (boffset(b)); 
}
void clear_bit(int b) { 
    data[bindex(b)] &= ~(1 << (boffset(b)));
}
int get_bit(int b) { 
    return data[bindex(b)] & (1 << (boffset(b));
}
void clear_all() { /* set all elements of data to zero */ }
void set_all() { /* set all elements of data to one */ }

Как написано, это немного грубо, поскольку он реализует только один глобальный битовый набор с фиксированным размером. Чтобы решить эти проблемы, вы должны начать со строки данных, например:

struct bitset { word_t *words; int nwords; };

, а затем написать функции для создания и уничтожения этих наборов битов.

struct bitset *bitset_alloc(int nbits) {
    struct bitset *bitset = malloc(sizeof(*bitset));
    bitset->nwords = (n / WORD_SIZE + 1);
    bitset->words = malloc(sizeof(*bitset->words) * bitset->nwords);
    bitset_clear(bitset);
    return bitset;
}

void bitset_free(struct bitset *bitset) {
    free(bitset->words);
    free(bitset);
}

Теперь относительно просто изменить предыдущие функции, приняв параметр struct bitset *. По-прежнему нет способа изменить размер набора битов в течение срока его службы, а также нет никаких проверок границ, но в этот момент добавить их будет несложно.

21 голосов
/ 14 апреля 2010

boost::dynamic_bitset, если длина известна только во время выполнения.

std::bitset, если длина известна во время компиляции (хотя и произвольно).

13 голосов
/ 28 декабря 2011

Я написал рабочую реализацию, основанную на ответе Дейла Хагглунда , чтобы предоставить битовый массив в C (лицензия BSD).

https://github.com/noporpoise/BitArray/

Пожалуйста, дайте мне знать, что вы думаете / дать предложения. Я надеюсь, что люди, которые ищут ответ на этот вопрос, найдут его полезным.

7 голосов
/ 04 мая 2012

Эта запись довольно старая, но в моей библиотеке ALFLB есть эффективный набор битовых массивов на C.

Для многих микроконтроллеров без кода операции аппаратного деления эта библиотека ЭФФЕКТИВНА, потому что она не использует деление: вместо этого используются маскирование и сдвиг битов. (Да, я знаю, что некоторые компиляторы преобразуют деление на 8 в смену, но это зависит от компилятора.)

Он был протестирован на массивах размером до 2 ^ 32-2 бит (около 4 миллиардов бит хранится в 536 МБ), хотя последние 2 бита должны быть доступны, если они не используются в цикле for в вашем приложении.

См. Ниже выдержку из документа. Doco - http://alfredo4570.net/src/alflb_doco/alflb.pdf,, библиотека - http://alfredo4570.net/src/alflb.zip

Наслаждайтесь,
Alf

//------------------------------------------------------------------
BM_DECLARE( arrayName, bitmax);
        Macro to instantiate an array to hold bitmax bits.
//------------------------------------------------------------------
UCHAR *BM_ALLOC( BM_SIZE_T bitmax); 
        mallocs an array (of unsigned char) to hold bitmax bits.
        Returns: NULL if memory could not be allocated.
//------------------------------------------------------------------
void BM_SET( UCHAR *bit_array, BM_SIZE_T bit_index);
        Sets a bit to 1.
//------------------------------------------------------------------
void BM_CLR( UCHAR *bit_array, BM_SIZE_T bit_index);
        Clears a bit to 0.
//------------------------------------------------------------------
int BM_TEST( UCHAR *bit_array, BM_SIZE_T bit_index); 
        Returns: TRUE (1) or FALSE (0) depending on a bit.
//------------------------------------------------------------------
int BM_ANY( UCHAR *bit_array, int value, BM_SIZE_T bitmax); 
        Returns: TRUE (1) if array contains the requested value (i.e. 0 or 1).
//------------------------------------------------------------------
UCHAR *BM_ALL( UCHAR *bit_array, int value, BM_SIZE_T bitmax);
        Sets or clears all elements of a bit array to your value. Typically used after a BM_ALLOC.  
        Returns: Copy of address of bit array
//------------------------------------------------------------------
void BM_ASSIGN( UCHAR *bit_array, int value, BM_SIZE_T bit_index);
        Sets or clears one element of your bit array to your value.
//------------------------------------------------------------------
BM_MAX_BYTES( int bit_max); 
        Utility macro to calculate the number of bytes to store bitmax bits.
        Returns: A number specifying the number of bytes required to hold bitmax bits.
//------------------------------------------------------------------
3 голосов
/ 14 апреля 2010

Вы можете использовать std :: bitset

int main() {
  const bitset<12> mask(2730ul); 
  cout << "mask =      " << mask << endl;

  bitset<12> x;

  cout << "Enter a 12-bit bitset in binary: " << flush;
  if (cin >> x) {
    cout << "x =        " << x << endl;
    cout << "As ulong:  " << x.to_ulong() << endl;
    cout << "And with mask: " << (x & mask) << endl;
    cout << "Or with mask:  " << (x | mask) << endl;
  }
}
2 голосов
/ 25 января 2014

Я знаю, что это старый пост, но я пришел сюда, чтобы найти простую реализацию набора битов Си, и ни один из ответов не вполне соответствовал тому, что я искал, поэтому я реализовал свой собственный, основываясь на ответе Дейла Хагглунда. Вот оно:)

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

typedef uint32_t word_t;
enum { BITS_PER_WORD = 32 };
struct bitv { word_t *words; int nwords; int nbits; };

struct bitv* bitv_alloc(int bits) {
    struct bitv *b = malloc(sizeof(struct bitv));

    if (b == NULL) {
        fprintf(stderr, "Failed to alloc bitv\n");
        exit(1);
    }

    b->nwords = (bits >> 5) + 1;
    b->nbits  = bits;
    b->words  = malloc(sizeof(*b->words) * b->nwords);

    if (b->words == NULL) {
        fprintf(stderr, "Failed to alloc bitv->words\n");
        exit(1);
    }

    memset(b->words, 0, sizeof(*b->words) * b->nwords);

    return b;
}

static inline void check_bounds(struct bitv *b, int bit) {
    if (b->nbits < bit) {
        fprintf(stderr, "Attempted to access a bit out of range\n");
        exit(1);
    }
}

void bitv_set(struct bitv *b, int bit) {
    check_bounds(b, bit);
    b->words[bit >> 5] |= 1 << (bit % BITS_PER_WORD);
}

void bitv_clear(struct bitv *b, int bit) {
    check_bounds(b, bit);
    b->words[bit >> 5] &= ~(1 << (bit % BITS_PER_WORD));
}

int bitv_test(struct bitv *b, int bit) {
    check_bounds(b, bit);
    return b->words[bit >> 5] & (1 << (bit % BITS_PER_WORD));
}

void bitv_free(struct bitv *b) {
    if (b != NULL) {
        if (b->words != NULL) free(b->words);
        free(b);
    }
}

void bitv_dump(struct bitv *b) {
    if (b == NULL) return;

    for(int i = 0; i < b->nwords; i++) {
        word_t w = b->words[i];

        for (int j = 0; j < BITS_PER_WORD; j++) {
            printf("%d", w & 1);
            w >>= 1;
        }

        printf(" ");
    }

    printf("\n");
}

void test(struct bitv *b, int bit) {
    if (bitv_test(b, bit)) printf("Bit %d is set!\n", bit);
    else                   printf("Bit %d is not set!\n", bit);
}

int main(int argc, char *argv[]) {
    struct bitv *b = bitv_alloc(32);

    bitv_set(b, 1);
    bitv_set(b, 3);
    bitv_set(b, 5);
    bitv_set(b, 7);
    bitv_set(b, 9);
    bitv_set(b, 32);
    bitv_dump(b);
    bitv_free(b);

    return 0;
}
1 голос
/ 07 октября 2016

В разработке микроконтроллеров иногда нам нужно использовать 2-мерный массив (матрица) со значением элемента только [0, 1]. Тот означает, что если мы используем 1 байт для типа элемента, это сильно тратит память (память микроконтроллера очень ограничена). Предлагаемое решение что мы должны использовать 1-битную матрицу (тип элемента 1-битный).

http://htvdanh.blogspot.com/2016/09/one-bit-matrix-for-cc-programming.html

1 голос
/ 20 сентября 2014

Я недавно выпустил BITSCAN, библиотеку битовых строк C ++, которая специально ориентирована на операции быстрого битового сканирования. BITSCAN доступен здесь . Он находится в альфа-версии, но все еще довольно хорошо протестирован, поскольку я использовал его в последние годы для исследований по комбинаторной оптимизации (например, в BBMC , современном алгоритме точного максимального клика). Сравнение с другими хорошо известными реализациями C ++ (STL или BOOST) можно найти здесь .

Надеюсь, вы найдете это полезным. Любые отзывы приветствуются.

1 голос
/ 26 августа 2014

Я использую это:

//#include <bitset>
#include <iostream>
//source /21296/kak-vy-ustanavlivaete-ochischaete-i-pereklychaete-odin-bit
#define BIT_SET(a,b) ((a) |= (1<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1<<(b)))
#define BIT_CHECK(a,b) ((a) & (1<<(b)))

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK(x,y) ((x) & (y))
...