2016-11-21 95 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++會導致這種混亂。