2016-11-21 6 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; 
} 

私が書き直したものは期待通りに動作していません。インプリメンテーションをテストするために、私はbitset_lookupを試して、0または1の戻り値を期待しますが、32の値を得ます。ポインタの使用とは何か関係があります。間違っている。

+3

これは、デバッガが役立つところです。 –

+0

1)否定的なことに気付かない場合は、ビットシフト/マスキングに符号付き整数を使用しないでください。 2)あなたが固定したい場合は、固定幅タイプを使用します。 3) 'WORDSIZE'と' sizeof(int) 'には関係がありません。 4)それがトゥルーリアのものであれば、それを忘れて、より良いものを手に入れよう。 – Olaf

答えて

2

これはチュートリアルではなく、誤解を招くほどの例です。

まず、符号なしの型を使用します。私はunsigned longをお勧めします(さまざまな理由で、どれも重要ではありません)。 <limits.h>ヘッダーファイルは定数CHAR_BITを定義し、符号なし整数型で使用できるビット数は常にCHAR_BIT * sizeof (unsigned_type)です。

第2に、サイズ情報を構造体に追加することによって、ビットマップ(または順序ビットセット)を動的にサイズ変更可能にすることができます。

上記のbitset構成において

#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; 
} 

に帰着、ulongulongsunsigned long複数の動的に割り当てられた配列です。したがって、それはulongs * ULONG_BITSビットを格納します。

BITSET_INITは、空のビットセットを初期化するために使用できるプリプロセッサマクロです。使用できない、または使用したくない場合は、bitset_init()を使用してビットセットを初期化できます。 2つは同等です。

bitset_free()は、ビットセットに割り当てられた動的メモリを解放します。呼び出しの後、ビットセットはなくなり、使用される変数は再初期化されます。 (free(NULL)の呼び出しは完全に安全であると何もしませんので、国連の使用が、初期化ビットセットにbitset_free()を呼び出すことは完全に大丈夫であることに注意してください。)

をOS /カーネルが自動的にプログラムによって使用されるすべてのメモリを解放しますので(特定の種類の共有メモリセグメントを除く)、プログラムが終了する直前にbitset_free()に電話する必要はありません。しかし、いくつかのアルゴリズムの一部としてビットセットを使用する場合は、不要なメモリを解放して、アプリケーションがメモリを「漏らす」(無駄にすることなく)無期限に実行できるようにすることは明らかです。

bitset_set()は、必要に応じて自動的にビットを大きくしますが、必要なだけ大きくします。これは必ずしも良い再配分方針ではありません:malloc()/realloc()などの呼び出しは比較的遅く、bitset_set()を昇順で(ビット数を増やすことによって)呼び出すと、ULONG_BITSごとにrealloc()が呼び出されます。代わりに、新しいサイズ(ulongs)を上向きに調整することをお勧めします。このの正確な公式は再割り当てポリシーですが、実際のプログラムでは実用的なテストが必要です。示されたものは動作し、かなり堅牢ですが、状況によっては少し遅いかもしれません。 (あなたはしかし、少なくとも数十ビットの何千ものを使用する必要があるだろう。)

bitset_get()関数の戻り値は、ファンキーである、私が望んでいたので、機能をするために同様の値を返すために、両方の "と「未設定」ビットセット "の外側にあります。なぜなら2つは論理的に似ているからです。 (すなわち、Iは、ビットセット、セットビットのセット考えるである;未設定としてセット外のすべてのビットを考えるのが論理的である場合)を

はるかに伝統的な定義は

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を返します。

!!に注意してください。演算子ではなく、奇妙なものではない。それを非not演算子にします。 !!xは、xが0の場合は0、xが0以外の場合は1です。

(単一ないオペレータ、!x、収率1ならxがゼロとxがゼロでない場合は0である。適用はない二回私は上記で説明していない、ではないが得られる。)

は上記を使用するには、例えば試します

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; 
} 

我々は(私はどこかgoofedない限り、あなたは私がやった気づけば、私は私のへまを修正することができますので、私はコメントで知らせて!)私たちが実行しているハードウェアやシステムについての仮定をしていないため、およびC標準が信頼できると言っているものだけですが、これは標準準拠のコンパイラでコードをコンパイルすることができるものであれば動作します。 Windows、Linux、BSD、古いUnix、macOSなどがあります。

いくつかの変更を加えて、マイクロコントローラでも動作させることができます。すべての開発ライブラリにrealloc()があるかどうかはわかりません。さらにmalloc()が利用できない場合があります。それとは別に、32ビットARMのようなものでは、これはうまくいくはずです。 8ビットのAVRでは、代わりにunsigned charCHAR_BITを使用することをお勧めします。これは、ハードウェアでサポートするのではなく、より大きなタイプをエミュレートする傾向があるからです。 (上のコードはうまくいくが、必要以上に遅くなる)

0

bitset_lookupからの戻り値は、数値ではなく、2進の真理値(つまり、いいえでyes)として扱われます。ゼロの場合、ビットは設定されません。ゼロでない場合、ビットが設定されます。本当にその関数はboolean != 0を返す必要があります。これにより、ゼロまたは1の値、現在のゼロまたは何もない実際のブール値(実際は(1 << (item & MASK)))になります。真のブール値を持たないC/C++では、このような混乱が生じる可能性があります。

関連する問題