簡體   English   中英

C/C++ 高效位數組

[英]C/C++ efficient bit array

你能推薦有效/干凈的方法來操作任意長度的位數組嗎?

現在我正在使用常規的 int/char 位掩碼,但是當數組長度大於數據類型長度時,這些位掩碼不是很干凈。

std vector<bool>對我不可用。

由於您提到了 C 和 C++,我將假設像boost::dynamic_bitset這樣的面向 C++ 的解決方案可能不適用,而是討論低級 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 *參數相對簡單。 仍然無法在其生命周期內重新調整位集的大小,也沒有任何邊界檢查,但此時添加兩者都不難。

boost::dynamic_bitset如果長度僅在運行時已知。

如果長度在編譯時已知(盡管是任意的),則為std::bitset

我已經根據Dale Hagglund 的響應編寫了一個工作實現,以提供 C 中的位數組(BSD 許可證)。

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

請讓我知道您的想法/提出建議。 我希望尋找這個問題的答案的人會發現它有用。

這篇文章相當陳舊,但在我的 ALFLB 庫中有一個高效的 C 位數組套件。

對於許多沒有硬件除法操作碼的微控制器,這個庫是有效的,因為它不使用除法:相反,使用了屏蔽和位移位。 (是的,我知道有些編譯器會將除以 8 轉換為移位,但這因編譯器而異。)

它已經在最多 2^32-2 位(大約 40 億位存儲在 536 兆字節中)的數組上進行了測試,盡管最后 2 位如果不在應用程序的 for 循環中使用,則應該可以訪問。

請參閱下面的 doco 摘錄。 Doco 是http://alfredo4570.net/src/alflb_doco/alflb.pdf ,庫是http://alfredo4570.net/src/alflb.zip

享受,
阿爾夫

//------------------------------------------------------------------
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.
//------------------------------------------------------------------

您可以使用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;
  }
}

我知道這是一篇舊帖子,但我來這里是為了找到一個簡單的 C bitset 實現,但沒有一個答案與我想要的完全匹配,所以我根據 Dale Hagglund 的答案實現了我自己的。 這里是 :)

#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;
}

我用這個:

//#include <bitset>
#include <iostream>
//source http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c
#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))

我最近發布了 BITSCAN,這是一個 C++ 位字符串庫,專門面向快速位掃描操作。 BITSCAN在這里可用。 它處於 alpha 階段,但仍然經過很好的測試,因為我近年來將它用於組合優化的研究(例如,在BBMC 中,一種最先進的精確最大集團算法)。 可以在此處找到與其他眾所周知的 C++ 實現(STL 或 BOOST)的比較。

希望對你有幫助。 歡迎任何反饋。

在微控制器開發中,有時我們只需要使用元素值為[0, 1]的二維數組(矩陣)。 這意味着如果我們使用 1 個字節作為元素類型,則會極大地浪費內存(微控制器的內存非常有限)。 建議的解決方案是我們應該使用 1 位矩陣(元素類型為 1 位)。

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

為此,我最近實現了一個名為 BitContainer 的僅包含頭文件的小型庫。 它側重於表現力和編譯時能力,可以在這里找到: https : //github.com/EddyXorb/BitContainer

它肯定不是查看位數組的經典方法,但可以用於強類型目的和命名屬性的內存高效表示。

例子:

constexpr Props props(Prop::isHigh(),Prop::isLow()); // intialize BitContainer of type Props with strong-type Prop

constexpr bool result1 = props.contains(Prop::isTiny()) // false
constexpr bool result2 = props.contains(Prop::isLow())  // true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM