當前位置: 首頁>>數據結構>>正文


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/zh-tw/article/58.html,未經允許,請勿轉載。