在创建位数组或在C中调用位集时,应使用哪种数据类型?我应该使用int
数组吗? unsigned int
? size_t
? uint8_t
? uintmax_t
?
对我来说,使用有符号整数类型是不可以的,正如SO中其他有关有符号整数左右移位的答案(我丢失了答案的链接)所指出的那样。
现在,我应该使用最小的可用无符号整数还是最大的无符号整数?哪一个表现最好?
我的一些想法:SO中的大多数位数组都使用char
或uint8_t
数组,但我看不出这比使用{{1} }(也是因为我还没有看到任何关于为什么要这样做的论点,因此还是这个问题)。当进行某些操作(例如两个位数组之间的并集和交集)时,使用较大的无符号整数时,循环将迭代次数减少。
编辑:看到一些答案后,我认为有些人对我的要求感到困惑。对于那个很抱歉。我想做的是制作一个位数组,其中每个位都可以单独访问或设置为1或0。我知道我可以只使用bool数组,但这并不节省空间。您可以说,如今我们的RAM足够大,位数组相对于布尔数组的增益很小,但这不是重点。
我想知道的是,给定一个位数组,其中每个位都可以由uintmax_t
进行修改或访问(与数组的索引不同),我的数组应该是哪种数据类型?
答案 0 :(得分:2)
这取决于您需要跟踪多少位,访问单个位的效率以及跟踪所有这些位要消耗的内存。
有很多方法可以做到,而无需进一步详细说明,这很难回答。
我看到的是一个简单的uint32_t
数组,以保持打包和良好的访问速度。然后,当访问一个位时,假设位128,这将是数组第4个uint32_t的位0。
答案 1 :(得分:2)
我个人会使用bsf
。对于大多数(不是全部,但可能是您关心的所有平台)平台,它的大小与CPU寄存器的大小相同,这意味着对于许多需要扫描整个位向量的操作而言,它可以实现最大位数每个循环迭代进行处理(例如查找设置位,计数位等)。对于此类算法,CPU内置clz
(向前扫描位)和unsigned long
(计数前导零)将大大加快算法的运行速度。
就上下文而言,Linux内核使用size_t
作为位向量,其AFAIK与所有Linux ABI上的alert()
相同,但在Windows上却并非如此(至少不适用于MSVC)即使在x64上也为32位。
答案 2 :(得分:1)
在位数组中使用哪种数据类型(?)
...每个位都可以单独访问或设置为1或0。
...只能使用布尔数组,但是空间效率不高。
您无法获得所需的一切:需要权衡。
对于N
位“数组”,各种方法
_Bool
的数组:_Bool ar1[N];
ar1[i]
unsigned char ar2[N];
最小类型的数组:unsigned char ar2[N];
ar2[i]
打包的unsigned char
数组:unsigned char ar3[(N+CHAR_BIT-1)/CHAR_BIT];
(ar3[i/CHAR_BIT] >> (i%CHAR_BIT))%2
打包的unsigned
数组:unsigned ar4[(N+UNSIGNED_BIT-1)/UNSIGNED_BIT];
ar3
类型可能比unsigned
快/快。(ar4[i/UNSIGNED_BIT] >> (i%UNSIGNED_BIT))%2
unsigned
必须基于UNSIGNED_BIT
而不是UNSIGNED_MAX
,因此人们担心CHAR_BIT
可能会被填充会导致更复杂的位宽确定。 结论
IMO,使用_Bool ar1[N];
,直到显示出空间/速度有问题为止。在这种情况下,我将移至unsigned ar4[(N+UNSIGNED_BIT-1)/UNSIGNED_BIT];
对我来说,使用整数类型是不可以的,正如SO中有关带符号整数左右移位的其他答案所述
在此夸大了OP的担忧。主要的移位问题出现在 signed 类型上。改用 unsigned 类型。
使用
char
或uint8_t
的数组,但是我看不出这比使用uintmax_t
更好。
大概OP在这里占用了一个“打包”的位数组。
Con for uintmax_t
。它要求数组大小是uintmax_t
的位大小与更容易匹配的uint8_t
的倍数。否则,两种方式都会浪费内存,uint8_t
只会减少内存消耗。
Con for uint8_t
。它并不总是可用(这是例外)。
Con for char
。它可能已签名
Con for uint8_t
。大概比unsigned
慢或慢。
Con for uintmax_t
。除非代码本身支持该宽类型,否则发出的代码可能比其他替代方法慢。
Con for uintmax_t
。宽类型更可能需要多个指令来缩小类型。当然,平台之间的差异很大。
最好使用最广泛的本机类型-通常为unsigned
。
IMO,unsigned
是包装的上乘之选。
答案 3 :(得分:1)
最好的选择是对尽可能多的方案进行基准测试。根据您打算存储多少位以及读取和写入它们的频率,将每个位存储为unsigned char
(甚至存储在unsigned int
中)可能很有意义,但是将其中的16个更紧密地包装在16位以上的unsigned int
中,这对于效率和易于访问的良好折衷是有意义的。 unsigned int
是一个不错的选择,但我不建议您使用unsigned int
,除非您可以保证您想要的体系结构不使用填充位或任何意外陷阱值。任何现代架构都可能有uint32_t
(在stdint.h
中定义),如果您不信任unsigned int
,这是我的建议,因为您知道它的确切大小并且可以保证没有填充位按标准。如果您知道将在64位体系结构上运行代码,那么uint64_t
将是一个更好的选择。如果可能,请记住进行基准测试。
请注意,该标准要求将小于int
的类型上的所有操作隐式转换(在C抽象机中)为int
(或unsigned int
,如果不合适) int
中的内容,然后再次转换回原始_Bool
,char
或short
。有时这可能会导致意外。
答案 4 :(得分:1)
通常,处理单个位时最有效的大小可能是unsigned int
。最大的大小和寄存器的大小可能效率低下(例如,在64位80x86上,64位指令需要“ REX前缀”,这将导致毫无意义的膨胀)。
对于处理整个位集(例如搜索,计数),如果性能首先是重要的,那么大小基本上就无关紧要。例如(对于SSE2),您可以将16个8位整数打包到128位寄存器中,或者将8个16位整数打包到128位寄存器中,或者将4个32位整数打包到128位寄存器中,或者将2个打包将64位整数放入128位寄存器中;对于所有这些情况,无论单个整数的大小如何,您都将执行128位运算。
但是,效率并不是唯一重要的事情,使用“不固定大小的整数”(例如unsigned int
)意味着您需要使用宏/ #define
来污染您的代码很难阅读(在“哦,该死,我需要集中精力,追踪埋在头文件中的一些随机噪声,只是为了看看FOO
到底是什么”),而固定大小整数类型(例如uint32_t
)将避免这种情况。具体来说,我会使用(并且已经使用过)uint32_t
而不关心性能。
您可以说,如今我们的RAM足够大,位数组比布尔数组的增益很小,但这不是重点。
您可以说RAM很大且相对较慢,缓存较小且相对较快,并且性能要求通过打包最多的数据来最小化缓存未命中(以提高缓存的效率并减少相对慢的RAM的使用)进入最小的空间。 ;)
答案 5 :(得分:0)
你是对的。通常对位数组使用char
或unsigned char
。这背后的原因纯粹是与效率有关。 char
仅保留1字节(8位)的内存,而int
通常需要4字节(32位,这取决于您的系统和编译器)
你做数学。您只需要存储一个位,那么哪一个使用效率更高?