2016-11-21 87 views
1

下面的代码创建并使用了一个位集,它来自以下教程"Intro to Bit Vectors"。我正在重写这段代码,试图学习和理解更多关于C结构和指针的知识。如何在C中实现位集合

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

#define WORDSIZE 32 
#define BITS_WORDSIZE 5 
#define MASK 0x1f 

// Create a bitset 
int initbv(int **bv, int val) { 
    *bv = calloc(val/WORDSIZE + 1, sizeof(int)); 
    return *bv != NULL; 
} 

// Place int 'i' in the biset 
void set(int bv[], int i) { 
    bv[i>>BITS_WORDSIZE] |= (1 << (i & MASK)); 
} 

// Return true if integer 'i' is a member of the bitset 
int member(int bv[], int i) { 
    int boolean = bv[i>>BITS_WORDSIZE] & (1 << (i & MASK)); 
    return boolean; 
} 

int main() { 
    int *bv, i; 

    int s1[] = {32, 5, 0}; 
    int s2[] = {32, 4, 5, 0}; 

    initbv(&bv, 32); 

    // Fill bitset with s1 
    for(i = 0; s1[i]; i++) { 
    set(bv, s1[i]); 
    } 

    // Print intersection of bitset (s1) and s2 
    for(i = 0; s2[i]; i++) { 
    if(member(bv, s2[i])) { 
     printf("%d\n", s2[i]); 
    } 
    } 

    free(bv); 
    return 0; 
} 

下面,就是我已经重写利用结构的。

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

#define WORDSIZE 32 
#define BITS_WS 5 
#define MASK 0x1f 

struct bitset { 
    int *bv; 
}; 

/* Create bitset that can hold 'size' items */ 
struct bitset * bitset_new(int size) { 
    struct bitset * set = malloc(sizeof(struct bitset)); 

    set->bv = calloc(size/WORDSIZE + 1, sizeof(int)); 

    return set; 
} 

/* Add an item to a bitset */ 
int bitset_add(struct bitset * this, int item) { 
    return this->bv[item>>BITS_WS] |= (1 << (item & MASK)); 
} 

/* Check if an item is in the bitset */ 
int bitset_lookup(struct bitset * this, int item) { 
int boolean = this->bv[item>>BITS_WS] & (1 << (item & MASK)); 
    printf("%d\n", boolean); 
    return boolean; 
} 

int main() { 
    struct bitset * test = bitset_new(32); 

    int num = 5; 
    bitset_add(test, num); 

    printf("%d\n", bitset_lookup(test, num)); 

    return 0; 
} 

我改写的内容不能按预期工作。为了测试实现,在main()中,我尝试了一个bitset_lookup,期望返回值为0或1,但是我得到的值为32.我相信这肯定与我使用指针有关,尽管我看不到我我做错了。

任何帮助表示赞赏!

+3

这是调试器可以提供帮助的地方。 –

+0

1)不要使用有符号整数为bitshifts /屏蔽,如果你不知道的负面影响的。 2)如果你使用固定宽度类型,则使用固定宽度类型。 3)'WORDSIZE'和'sizeof(int)'之间没有关系。 4)如果是那样的话,忘了它并得到一个更好的... – Olaf

回答

2

这不是教程,充其量只是误导性的例子。

首先,使用无符号类型。我建议unsigned long(出于各种原因,它们都不重要)。 <limits.h>头文件定义常量CHAR_BIT,并且您可以在任何无符号整数类型中使用的位数始终为CHAR_BIT * sizeof (unsigned_type)。其次,通过将尺寸信息添加到结构中,可以使位图(或有序位集)动态调整大小。

上述归结为

#include <stdlib.h> 
#include <limits.h> 

#define ULONG_BITS (CHAR_BIT * sizeof (unsigned long)) 

typedef struct { 
    size_t   ulongs; 
    unsigned long *ulong; 
} bitset; 

#define BITSET_INIT { 0, NULL } 

void bitset_init(bitset *bset) 
{ 
    if (bset) { 
     bset->ulongs = 0; 
     bset->ulong = NULL; 
    } 
} 

void bitset_free(bitset *bset) 
{ 
    if (bset) { 
     free(bset->ulong); 
     bset->ulongs = 0; 
     bset->ulong = NULL; 
    } 
} 

/* Returns: 0 if successfully set 
      -1 if bs is NULL 
      -2 if out of memory. */ 
int bitset_set(bitset *bset, const size_t bit) 
{ 
    if (bset) { 
     const size_t i = bit/ULONG_BITS; 

     /* Need to grow the bitset? */ 
     if (i >= bset->ulongs) { 
      const size_t ulongs = i + 1; /* Use better strategy! */ 
      unsigned long *ulong; 
      size_t   n = bset->ulongs; 

      ulong = realloc(bset->ulong, ulongs * sizeof bset->ulong[0]); 
      if (!ulong) 
       return -2; 

      /* Update the structure to reflect the changes */ 
      bset->ulongs = ulongs; 
      bset->ulong = ulong; 

      /* Clear the newly acquired part of the ulong array */ 
      while (n < ulongs) 
       ulong[n++] = 0UL; 
     } 

     bset->ulong[i] |= 1UL << (bit % ULONG_BITS); 

     return 0; 
    } else 
     return -1; 
} 

/* Returns: 0 if SET 
      1 if UNSET 
      -1 if outside the bitset */ 
int bitset_get(bitset *bset, const size_t bit) 
{ 
    if (bset) { 
     const size_t i = bit/ULONG_BITS; 

     if (i >= bset->ulongs) 
      return -1; 

     return !(bset->ulong[i] & (1UL << (bit % ULONG_BITS))); 
    } else 
     return -1; 
} 

bitset结构中,ulongulongsunsigned long秒的动态分配的数组。因此,它存储ulongs * ULONG_BITS位。

BITSET_INIT是一个预处理宏,可以用来初始化一个空的位集。如果你不能或不想使用它,你可以使用bitset_init()来初始化一个bitset。这两个是相同的。

bitset_free()释放分配给位集的动态存储器。调用之后,位设置消失了,所使用的变量重新初始化。 (请注意,在未使用但已初始化的位集合上调用bitset_free()完全可以,因为调用free(NULL)是完全安全的并且什么都不做)。

由于OS /内核会自动释放程序使用的所有内存(除了某些类型的共享内存段以外),在程序退出之前不必调用bitset_free()。但是,如果使用位集作为算法的一部分,那么释放不再需要的内存显然是一个好习惯,这样应用程序就可以无限期地运行而不会“泄漏”(浪费)内存。

bitset_set()在必要时会自动增加位集,但只能根据需要增大。这未必是一个很好的再分配政策:malloc()/realloc()等通话是比较慢的,如果你碰巧打电话bitset_set()中(通过增加位数)递增的顺序,你最终调用realloc()每一个ULONG_BITS。相反,它往往是一个好主意,调整新的大小(ulongs)向上 - 您使用此的精确公式为你的再分配政策 - ,但暗示了良好的政策需要有切实可行的方案实际测试。所示的工作,并且非常强大,但在某些情况下可能会有点慢。 (您可能需要使用位中至少几万,虽然)。

bitset_get()函数返回值是时髦的,因为我想要的功能,为返回一个类似的值都“未设置”“在位集外“,因为两者在逻辑上相似。 (也就是说,我考虑位设置,一组一组位;在这种情况下,它是合乎逻辑的思考设定为未设置以外的所有位)

一个更传统的定义是

int bitset_get(bitset *bset, const size_t bit) 
{ 
    if (bset) { 
     const size_t i = bit/ULONG_BITS; 

     if (i >= bset->ulongs) 
      return 0; 

     return !!(bset->ulong[i] & (1UL << (bit % ULONG_BITS))); 
    } else 
     return 0; 
} 

,它仅返回1的位集,0返回集外的位。

注意!!。这只是两个不算算,没什么太奇怪的;使它成为一个非运营商。 !!x是0,如果x是零,和1,如果x非零。

(A单不操作,!x,产率1如果x是零,和0,如果x非零。应用不是两次得到的未未予如上文所述。)

为了使用上文中,尝试例如

int main(void) 
{ 
    bitset train = BITSET_INIT; 

    printf("bitset_get(&train, 5) = %d\n", bitset_get(&train, 5)); 

    if (bitset_set(&train, 5)) { 
     printf("Oops; we ran out of memory.\n"); 
     return EXIT_FAILURE; 
    } else 
     printf("Called bitset_set(&train, 5) successfully\n"); 

    printf("bitset_get(&train, 5) = %d\n"); 

    bitset_free(&train); 

    return EXIT_SUCCESS; 
} 

因为我们不做出关于硬件或系统,我们正在运行的任何假设(除非我疯玩的地方;如果你发现我做,让我在评论中知道,所以我可以解决我的混日子!),和只有C标准说我们可以依赖的东西,这应该适用于任何可以用符合标准的编译器编译代码的东西。 Windows,Linux,BSD,旧的Unix,macOS等等。

有了一些变化,可以做出对微控制器的工作,甚至。我不确定是否所有开发库都有realloc();即使malloc()可能无法使用。除此之外,对于像32位ARM这样的应用来说,这应该是正常的;在8位AVR上,使用unsigned charCHAR_BIT是一个好主意,因为它们倾向于模拟较大的类型,而不是在硬件中支持它们。 (上面的代码可以工作,但是比必要的慢。)

0

bitset_lookup返回的值应该被视为一个二元真值(即否),而不是数值。如果它为零,则该位未设置;如果不是零,则该位被设置。真的那个函数应该返回boolean != 0,这将强制它为零或一个值,一个真正的布尔值,而不是当前零或任何东西(实际上(1 << (item & MASK)))。没有真正的布尔值的C/C++会导致这种混乱。