【问题标题】:C/C++ efficient bit arrayC/C++ 高效位数组
【发布时间】:2011-02-07 16:05:48
【问题描述】:

你能推荐一种有效/干净的方法来操作任意长度的位数组吗?

现在我正在使用常规的 int/char 位掩码,但是当数组长度大于数据类型长度时,这些位掩码不是很干净。

std vector<bool> 对我不可用。

【问题讨论】:

  • 当数组长度大于数据类型长度时,当您说“常规 int/char 位掩码”不是很干净时,我不太清楚您的意思是什么?我在下面发布了一个传统的 C 位集实现,因为我解释了您对 C/C++ 解决方案的请求以及您声明 std::vector<bool> 不可用以表明您可能需要一个直接的 C 解决方案。

标签: c++ c arrays bit


【解决方案1】:

我最近为此目的实现了一个名为 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

【讨论】:

    【解决方案2】:

    在微控制器开发中,有些时候我们需要用到 仅元素值为 [0, 1] 的二维数组(矩阵)。那 意味着如果我们使用 1 字节作为元素类型,它会极大地浪费内存 (微控制器的内存非常有限)。建议的解决方案是 我们应该使用 1 位矩阵(元素类型为 1 位)。

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

    【讨论】:

      【解决方案3】:

      我最近发布了 BITSCAN,这是一个专门针对快速位扫描操作的 C++ 位字符串库。 BITSCAN 可用here。它处于 alpha 阶段,但仍然经过很好的测试,因为我近年来将它用于组合优化研究(例如,在BBMC 中,一种最先进的精确最大集团算法)。可以在here 找到与其他知名 C++ 实现(STL 或 BOOST)的比较。

      我希望你觉得它有用。欢迎任何反馈。

      【讨论】:

      • biicode.comexpired,现在是一个停车场。看起来 BITSCAN 现在可以通过 github.com/psanse/bitscan 获得。
      【解决方案4】:

      我用这个:

      //#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))
      

      【讨论】:

      • 我们为什么要使用它?在这里解释一下!
      • 在大多数实现中,一个布尔值需要 1 个字节,在这种方法中,所需的内存空间最多可以减少 8 倍,但会以一些速度为代价。
      【解决方案5】:

      我知道这是一篇旧帖子,但我来这里是为了找到一个简单的 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;
      }
      

      【讨论】:

        【解决方案6】:

        boost::dynamic_bitset 如果长度仅在运行时已知。

        std::bitset 如果长度在编译时已知(尽管是任意的)。

        【讨论】:

        • 谢谢。我不能直接使用(GPU设备),但我可以查看源代码
        • @aaa:你可以使用.to_ulong()来获取设备的数值,假设小于32位。
        • 运行时函数需要特殊的关键字,所以在这个意义上我不能直接使用 bitset
        【解决方案7】:

        由于您提到了 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 * 参数相对简单。仍然无法在其生命周期内重新调整位集的大小,也没有任何边界检查,但此时两者都不会难以添加。

        【讨论】:

        • 为了改进这个答案,我会使用 CHAR_BIT (limits.h) 而不是 8。您可能在一个字节不是 8 位的架构上。
        【解决方案8】:

        我已经编写了一个基于Dale Hagglund's response 的工作实现,以在 C 中提供一个位数组(BSD 许可证)。

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

        请让我知道您的想法/提出建议。我希望寻找这个问题的答案的人会发现它很有用。

        【讨论】:

        • 谢谢!!!你为我节省了几个小时的编码时间。我会检查你的代码,等待我的 cmets ;)
        • 它似乎假定了一个小端处理器并且在一个大端处理器上失败了。
        • @JonS 请在 github 上打开一个问题,指定哪些测试失败 - 它应该支持大端机器。不幸的是,我没有一个可以测试。
        【解决方案9】:

        这篇文章比较老了,但是在我的 ALFLB 库中有一个高效的 C 位数组套件。

        对于许多没有硬件除法操作码的微控制器,这个库是高效的,因为它不使用除法:而是使用掩码和位移。 (是的,我知道有些编译器会将除以 8 转换为移位,但这因编译器而异。)

        它已经在最多 2^32-2 位的数组上进行了测试(大约 40 亿位存储在 536 MBytes 中),尽管如果您的应用程序的 for 循环中没有使用最后 2 位,则应该可以访问。

        请参阅下面的文档摘录。 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.
        //------------------------------------------------------------------
        

        【讨论】:

        • "一些编译器会将除以 8 转换为移位" 世纪 编写的不这样做? :)
        【解决方案10】:

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

        【讨论】:

        • 你编译过这个吗? bitset 是否支持按位与和或?
        • 你编译过这个吗?不,bitset 是否支持按位与和或?是的,有 operator& 和 operator|此处记录的重载sgi.com/tech/stl/bitset.html
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-11
        • 1970-01-01
        • 2020-09-07
        • 1970-01-01
        • 2021-05-08
        • 2010-09-09
        相关资源
        最近更新 更多