如何在 C 中实现位集?
Posted
技术标签:
【中文标题】如何在 C 中实现位集?【英文标题】:How to implement a bitset in C? 【发布时间】:2011-05-21 07:50:34 【问题描述】:我一直在 Java 中使用 Bitset 类,我想在 C 中做类似的事情。我想我必须像 C 中的大多数东西一样手动完成。什么是一种有效的实现方式?
byte bitset[]
也许
bool bitset[]
?
【问题讨论】:
内存或CPU效率高吗? @robert:我想首先是在内存方面。这是因为可能的处理开销很小,但在缓存未命中的情况下开销很大。 @robert:有区别吗?如果有大量位,性能将受到缓存未命中的限制,因此尽可能紧密地打包位将提供最佳性能。只有当比特很少时,每比特使用一个完整字节(或更多)可能更有效。 【参考方案1】:CCAN 有一个可以使用的 bitset 实现:http://ccan.ozlabs.org/info/jbitset.html
但如果您最终自己实现它(例如,如果您不喜欢该包的依赖项),您应该使用整数数组并使用计算机架构的本机大小:
#define WORD_BITS (8 * sizeof(unsigned int))
unsigned int * bitarray = (int *)calloc(size / 8 + 1, sizeof(unsigned int));
static inline void setIndex(unsigned int * bitarray, size_t idx)
bitarray[idx / WORD_BITS] |= (1 << (idx % WORD_BITS));
不要使用特定的大小(例如使用 uint64 或 uint32),让计算机使用它想要使用的大小并使用 sizeof 来适应它。
【讨论】:
可能,但也可能您想要可以有效操作的最大尺寸。如果您正在扫描位,那么这可能是有效的。再说一次,一些 CPU 从内存中加载缓存的方式与您选择的大小无关。但另一方面……也许你只需要试验和测量。 当然是实验,但根据我的经验,使用单词大小进行拆分通常是最快的。我不确定我是否理解您的第一点?sizeof
以字节为单位,而不是位。您需要乘以 8(或者在某些表达式中更一般地说是 CHAR_BIT
。
calloc
的第一个参数不是错了吗?我认为应该是(size + WORD_BITS - 1) / WORD_BITS
,因为这是所需的无符号整数的数量。
也可以将(idx % WORD_BITS)
简化为(idx & (WORD_BITS - 1))
,但一个好的编译器可能会自动进行优化。【参考方案2】:
嗯,byte bitset[] 似乎有点误导,不是吗?
在结构中使用位字段,然后您可以维护这些类型的集合(或在您认为合适的情况下使用它们)
struct packed_struct
unsigned int b1:1;
unsigned int b2:1;
unsigned int b3:1;
unsigned int b4:1;
/* etc. */
packed;
【讨论】:
这对于一小部分标志来说不是一个坏主意,但是如果你使用一个位集,你通常希望它可以被一个整数索引。例如,参见 Java bitset 类。 是的,我后来想了想,然后注意到迈克发布了一些类似的内容。 在变量名中使用位域和索引会适得其反。【参考方案3】:将其设为 unsigned int 64 数组。
【讨论】:
【参考方案4】:像往常一样,您需要首先决定您需要对您的 bitset 执行哪种操作。也许是 Java 定义的一些子集?之后,您可以决定如何最好地实施它。您当然可以查看 OpenJDK 中 BitSet.java 的源代码。
【讨论】:
【参考方案5】:没有人提到 C FAQ 推荐的内容,这是一堆老旧的宏:
#include <limits.h> /* for CHAR_BIT */
#define BITMASK(b) (1 << ((b) % CHAR_BIT))
#define BITSLOT(b) ((b) / CHAR_BIT)
#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
#define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b))
#define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))
#define BITNSLOTS(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT)
(通过http://c-faq.com/misc/bitsets.html)
【讨论】:
但这并不总能防止宏观副作用,例如尝试:int i = 0, bits; BITSET(bits, i++)
@LukeSmith 你说得有道理,但它看起来相当广泛使用。似乎实现宏的正确方法是让调用者理解它是一个宏,从而将责任推给调用者。 (任何不喜欢的人,可以将它包装在一个内联函数中)【参考方案6】:
您可以使用bitsPerItem
或1
尝试我的PackedArray 代码。
它实现了一个随机访问容器,其中项目以位级别打包。换句话说,它就像您能够操纵例如uint9_t
或 uint17_t
数组:
PackedArray principle:
. compact storage of <= 32 bits items
. items are tightly packed into a buffer of uint32_t integers
PackedArray requirements:
. you must know in advance how many bits are needed to hold a single item
. you must know in advance how many items you want to store
. when packing, behavior is undefined if items have more than bitsPerItem bits
PackedArray general in memory representation:
|-------------------------------------------------- - - -
| b0 | b1 | b2 |
|-------------------------------------------------- - - -
| i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 |
|-------------------------------------------------- - - -
. items are tightly packed together
. several items end up inside the same buffer cell, e.g. i0, i1, i2
. some items span two buffer cells, e.g. i3, i6
【讨论】:
【参考方案7】:我推荐我的BITSCAN C++ library(1.0 版刚刚发布)。 BITSCAN 专门针对快速位扫描操作。我已经用它来实现涉及简单无向图的 NP-Hard 组合问题,例如最大团(参见 BBMC 算法,了解领先的精确求解器)。
BITSCAN 与标准解决方案 STL bitset 和 BOOST dynamic_bitset 的比较可在此处获得: http://blog.biicode.com/bitscan-efficiency-at-glance/
【讨论】:
以上是关于如何在 C 中实现位集?的主要内容,如果未能解决你的问题,请参考以下文章