当前位置: 首页>>数据结构>>正文


BitSet(位图、位集、位域)的C语言实现(原创)

在处理大数据量问题时,用BitSet做标记处理,可以节约内存空间。BitSet的中文翻译有位图、位集、位域等,个人觉得位图比较合适,下文中将以位图作为BitSet的名称。
实现位图的基本思想是,分配一块连续的内存空间,对每一个字节(BYTE)采用位运算设置其中每个位(BIT)的值(0或者1)。下面以位图的C源码说明具体的实现方式。

一、数据结构 


typedef struct BitSetStruct BitSet; 
struct BitSetStruct { 
   char *_mblock; // a block of memmery 
   int _len; //the number of bits in specified by users 
   int _len2;//the number of bytes in the _mblock 
   int _count; //the number of bits that is set to 1 in the _mblock
}; 

其中:
_mblock是一块动态分配的连续内存块。
_len是可自定义的位(BIT)的数目。
_len2为_mblock实际分配的字节(BYTE)数。
_count为被设置为1的位的个数。
 

二、函数集


BitSet *bitset_new(int bitsCount);          //构造函数,bitsCount对应_len,即指定的位数。
BitSet *bitset_new2(const char *bits);    //构造函数,以0/1字符串构造位图,如bitset_new2("10101110")。 
void bitset_free(BitSet *bs);                   //析构函数,释放内存空间。 
int bitset_size(BitSet *bs);                      //返回位数, 对应_len的值。 
int bitset_count(BitSet *bs);                   //返回设置为1的位数,对应_count。 
void bitset_set_all(BitSet *bs);               //将所有位设置为1。 
void bitset_reset_all(BitSet *bs);            将所有位设置为0。 
void bitset_reset(BitSet *bs, int pos);     将位图中的第pos位设置为0(pos从0开始)。 
void bitset_set(BitSet *bs, int pos);        将位图中的第pos设置为1(pos从0开始)。 
int bitset_isset(BitSet *bs, int pos);        判断位图中的第pos位是否为1(同样,pos从0开始)。如果该位为1,则返回1;否则返回0。 
void bitset_print(BitSet *bs);                  输出位图中的每个位的内容。 
char *bitset_to_str(BitSet *bs);              将位图转化为字符串,返回的指针需要free。 

三、重要的常量


	#define BITS_PER_CHAR   8                每个CHAR(对应一个BYTE)的位数。 

	static const unsigned char mask1[8] =
{
   0x01, /* 00000001 */
   0x02, /* 00000010 */
   0x04, /* 00000100 */
   0x08, /* 00001000 */
   0x10, /* 00010000 */
   0x20, /* 00100000 */
   0x40, /* 01000000 */
   0x80  /* 10000000 */
};

位运算的掩码,ch |= mask1[i]可以将ch的第i为设置为1, ch &mars[i]可以判断ch的第i位是否为1。
  


	static const unsigned char mask2[8] =
{
   0xFE, /* 11111110 */
   0xFD, /* 11111101 */
   0xFB, /* 11111011 */
   0xF7, /* 11110111 */
   0xEF, /* 11101111 */
   0xDF, /* 11011111 */
   0xBF, /* 10111111 */
   0x7F  /* 01111111 */
};

位运算掩码,ch &=mask2[i]可以将第i位设置为0 

源代码有三个文件,使用VC++6.0编译。其中,bitset.h是数据结构和函数声明,bitset.c是函数的实现,main.c是测试文件,可以看到,在代码的设计上我们采用了基于对象的编程思想。  

/*
 * File: bitset.h
 * Purpose: implementation of bitset in C
 * Author: puresky
 * Date: 2011/05/03
 */

#ifndef _BIT_SET_H
#define _BIT_SET_H

#define BITS_PER_CHAR 8

typedef struct BitSetStruct BitSet;
struct BitSetStruct
{
      char *_mblock; // a block of memmery
      int _len; //the number of bits specified by users
      int _len2;//the number of bytes in the _mblock 
      int _count; //the number of bits that is set to 1 in the _mblock
};

BitSet *bitset_new(int bitsCount);
BitSet *bitset_new2(const char *bits);
void bitset_free(BitSet *bs);
int bitset_size(BitSet *bs);
int bitset_count(BitSet *bs);
void bitset_reset(BitSet *bs, int pos);
void bitset_reset_all(BitSet *bs);
void bitset_set(BitSet *bs, int pos);
void bitset_set_all(BitSet *bs);
void bitset_reset(BitSet *bs, int pos);
int bitset_isset(BitSet *bs, int pos);
void bitset_print(BitSet *bs);
char *bitset_to_str(BitSet *bs);
#endif

/*
 * File: bitset.c
 * Purpose: implementation of bitset in C
 * Author: puresky
 * Date: 2011/05/03
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bitset.h"


static const unsigned char mask1[8] =
{
     0x01, /* 00000001 */
     0x02, /* 00000010 */
     0x04, /* 00000100 */
     0x08, /* 00001000 */
     0x10, /* 00010000 */
     0x20, /* 00100000 */
     0x40, /* 01000000 */
     0x80  /* 10000000 */
};

static const unsigned char mask2[8] =
{
      0xFE, /* 11111110 */
      0xFD, /* 11111101 */
      0xFB, /* 11111011 */
      0xF7, /* 11110111 */
      0xEF, /* 11101111 */
      0xDF, /* 11011111 */
      0xBF, /* 10111111 */
      0x7F  /* 01111111 */
};

BitSet *bitset_new(int bitsCount)
{
      BitSet *bs = (BitSet *)malloc(sizeof(BitSet));
      bs->_count = 0;
      bs->_len = bitsCount;
      // the real length of _mblock is (bitsCount / BITS_PER_CHAR + 1) in Byte
      bs->_len2 = bs->_len / BITS_PER_CHAR + 1;
      bs->_mblock = (char *)malloc(sizeof(char) * bs->_len2);
      memset(bs->_mblock, 0, sizeof(char) * bs->_len2);
     
      return bs;
}

BitSet *bitset_new2(const char *bits)
{
      int i, len;
      BitSet *bs;
      len = strlen(bits);
      bs = bitset_new(len);
      for(i = 0; i < len; ++i)
            if(bits[i] == '1')
                  bitset_reset(bs, i);
      return bs;
}

void bitset_free(BitSet *bs)
{
      if(bs)
      {
            free(bs->_mblock);
            free(bs);
      }
}

int bitset_size(BitSet *bs)
{
      return bs->_len;
}

int bitset_count(BitSet *bs)
{
      return bs->_count;
}

void bitset_reset_all(BitSet *bs)
{
      memset(bs->_mblock, 0x00, bs->_len2);
      bs->_count = 0;
}

void bitset_reset(BitSet *bs, int pos)
{
      int i, j;

      if(pos >= bs->_len)
      {
            fprintf(stderr, "bit postion :%d is invalid!\n", pos);
            return;
      }

      i = pos / BITS_PER_CHAR;
      j = pos - i * BITS_PER_CHAR;
      if(bitset_isset(bs, pos))
      {
            bs->_mblock[i] &= mask2[j];
            bs->_count--;
      }

}

void bitset_set_all(BitSet *bs)
{
      memset(bs->_mblock, 0xFF, bs->_len2);
      bs->_count = bs->_len;
}

void bitset_set(BitSet *bs, int pos)
{
      int i, j;

      if(pos >= bs->_len)
      {
            fprintf(stderr, "bit postion :%d is invalid!\n", pos);
            return;
      }

      i = pos / BITS_PER_CHAR;
      j = pos - i * BITS_PER_CHAR;
      if(!bitset_isset(bs, pos))
      {     
            bs->_count++;
            bs->_mblock[i] |= mask1[j];
      }
}

int bitset_isset(BitSet *bs, int pos)
{
      int i, j;
     
      if(pos >= bs->_len)
      {
            fprintf(stderr, "bit postion :%d is invalid!\n", pos);
            return 0;
      }

      i = pos / BITS_PER_CHAR;
      j = pos - i * BITS_PER_CHAR;
      if(bs->_mblock[i] & mask1[j])
            return 1;
      return 0;
}

void bitset_print(BitSet *bs)
{
      int i;
      for(i = 0; i < bs->_len; ++i)
            printf("%c", (bitset_isset(bs, i) ? '1' : '0'));
      printf("\n");
}

char *bitset_to_str(BitSet *bs)
{
      int i;
      char *str = (char *)malloc(sizeof(char) * (bs->_len + 1));
      memset(str, 0, sizeof(char) * (bs->_len + 1));
      for(i = 0; i < bs->_len; ++i)
            str[i] = (bitset_isset(bs, i) ? '1' : '0');
      return str;
}

/*
 * File: main.c
 * Purpose: testing bitset in C
 * Author: puresky
 * Date: 2011/05/03
 */

#include <stdio.h>
#include <stdlib.h>
#include "bitset.h"

int main(int argc, char **argv)
{
      char *str;
      int n;
      BitSet *bs = bitset_new(101);
     
      // 1. bitset_set_all
      bitset_set_all(bs);
      bitset_print(bs);

      // 2. bitset_reset_all
      bitset_reset_all(bs);
      bitset_print(bs);

      // 3. bitset_set
      bitset_set(bs, 80);
      bitset_set(bs, 90);
      bitset_set(bs, 30);
      bitset_set(bs, 0);
      bitset_set(bs, 100);
      bitset_print(bs);

      // 4.bitset_reset
      bitset_reset(bs, 90);
      bitset_reset(bs, 100);
      bitset_print(bs);


      // 5. bitset_count
      n = bitset_count(bs);
      printf("count:%d\n", n);

      // 6. bitset_isset
      printf("%d\n", bitset_isset(bs, 80));
      printf("%d\n", bitset_isset(bs, 90));
     

      // 7. bitset_to_str
      str = bitset_to_str(bs);
      printf("str:%s\n", str);
      free(str);

      bitset_free(bs);
      system("pause");
      return 0;
}
本文由《纯净天空》出品。文章地址: https://vimsky.com/article/58.html,未经允许,请勿转载。