用于存储占用1位空间的位的C hack
C hack for storing a bit that takes 1 bit space?
我有一长串0到67600之间的数字。现在我想用一个长度为67600个元素的数组来存储它们。如果某个数字在集合中,则该元素被设置为1;如果该数字不在集合中,则该元素被设置为0。ie。每次我只需要1bit的信息来存储一个数字的存在。在C/c++中有什么hack可以帮助我实现这一点吗?
在c++中,如果大小是动态的,可以使用std::vector<bool>
(这是std::vector
的特殊情况,请参阅此),否则可以使用std::bitset
(如果可能的话,首选std::bitset
)。如果您需要在运行时设置/更改大小,也有boost::dynamic_bitset
。你可以在这里找到有关它的信息,它非常酷!
在C(和c++)中,您可以使用位操作符手动实现此操作。下面是常见操作的一个很好的总结。我想提的一件事是,当你做位运算时,使用无符号整数是个好主意。移位负整数时,<<
和>>
没有定义。你需要分配一些整型的数组,比如uint32_t
。如果你想存储N
位,它将占用这些uint32_t
位中的N/32
位。i
位存储在i / 32
位uint32_t
的i % 32
位。您可能希望根据您的体系结构和其他约束使用不同大小的整型。注意:更喜欢使用现有的实现(例如,正如第一段中描述的c++,搜索C解决方案)而不是自己的(除非你特别想要,在这种情况下,我建议在解决这个问题之前从其他地方学习更多关于二进制/位操作的知识)。这类事情已经做得很烂了,也有"好的"解决方案。
有许多技巧可以可能只消耗一个位:例如位字段数组(也适用于C),但是否使用更少的空间取决于编译器。请看这个链接。
请注意,无论你做什么,你几乎肯定永远无法使用正好 N位来存储N位信息-你的计算机很可能无法分配少于8位:如果你想要7位,你将不得不浪费1位,如果你想要9位,你将不得不采取16位,浪费其中的7位。即使您的计算机(CPU + RAM等)可以在单个比特上"操作",如果您运行在malloc
/new
操作系统中,由于开销,您的分配器跟踪数据到如此小的精度也是不明智的。最后一个条件是相当愚蠢的-你不会找到一个正在使用的架构,允许你一次操作少于8位,我想:)
您应该使用std::bitset
。
std::bitset
的功能类似于bool
的数组(实际上类似于std::array
,因为它是按值复制的),但每个元素只使用1位存储空间。
另一个选项是vector<bool>
,我不推荐,因为:
- 它使用较慢的指针间接和堆内存来调整大小,这是你不需要的。
- 这种类型经常被标准纯粹主义者中伤,因为它声称是标准容器,但却没有遵守标准容器的定义*。
*例如,一个符合标准的函数可以期望&container.front()
产生一个指向任何容器类型的第一个元素的指针,而std::vector<bool>
则不能。
事实上有!std::vector<bool>
对此有专门化:http://en.cppreference.com/w/cpp/container/vector_bool
查看文档,它尽可能高效地存储文档。
编辑:正如其他人所说,std::bitset
也可用:http://en.cppreference.com/w/cpp/utility/bitset
如果你想用C语言写它,有一个长度为67601位的char数组(67601/8 = 8451),然后为每个值打开/关闭相应的位
其他人给出了正确的想法。这是我自己实现的bitsarr
,或"数组"位。无符号字符是一个字节,所以它本质上是一个无符号字符数组,它将信息存储在单个比特中。除了1位值之外,我还添加了存储2位或4位值的选项,因为它们都可以除以8(字节的大小),如果你想存储大量的整数,范围从0-3或0-15,这将是有用的。
当设置和获取时,数学运算是在函数中完成的,所以你可以给它一个索引,就像它是一个普通数组一样——它知道去哪里找。
另外,传递给set的值不能太大,否则会破坏其他值,这是用户的责任。可以修改它,让overflow循环回到0左右,但这只会让它变得更复杂,所以我决定相信自己。
#include<stdio.h>
#include <stdlib.h>
#define BYTE 8
typedef enum {ONE=1, TWO=2, FOUR=4} numbits;
typedef struct bitsarr{
unsigned char* buckets;
numbits n;
} bitsarr;
bitsarr new_bitsarr(int size, numbits n)
{
int b = sizeof(unsigned char)*BYTE;
int numbuckets = (size*n + b - 1)/b;
bitsarr ret;
ret.buckets = malloc(sizeof(ret.buckets)*numbuckets);
ret.n = n;
return ret;
}
void bitsarr_delete(bitsarr xp)
{
free(xp.buckets);
}
void bitsarr_set(bitsarr *xp, int index, int value)
{
int buckdex, innerdex;
buckdex = index/(BYTE/xp->n);
innerdex = index%(BYTE/xp->n);
xp->buckets[buckdex] = (value << innerdex*xp->n) | ((~(((1 << xp->n) - 1) << innerdex*xp->n)) & xp->buckets[buckdex]);
//longer version
/*unsigned int width, width_in_place, zeros, old, newbits, new;
width = (1 << xp->n) - 1;
width_in_place = width << innerdex*xp->n;
zeros = ~width_in_place;
old = xp->buckets[buckdex];
old = old & zeros;
newbits = value << innerdex*xp->n;
new = newbits | old;
xp->buckets[buckdex] = new; */
}
int bitsarr_get(bitsarr *xp, int index)
{
int buckdex, innerdex;
buckdex = index/(BYTE/xp->n);
innerdex = index%(BYTE/xp->n);
return ((((1 << xp->n) - 1) << innerdex*xp->n) & (xp->buckets[buckdex])) >> innerdex*xp->n;
//longer version
/*unsigned int width = (1 << xp->n) - 1;
unsigned int width_in_place = width << innerdex*xp->n;
unsigned int val = xp->buckets[buckdex];
unsigned int retshifted = width_in_place & val;
unsigned int ret = retshifted >> innerdex*xp->n;
return ret; */
}
int main()
{
bitsarr x = new_bitsarr(100, FOUR);
for(int i = 0; i<16; i++)
bitsarr_set(&x, i, i);
for(int i = 0; i<16; i++)
printf("%dn", bitsarr_get(&x, i));
for(int i = 0; i<16; i++)
bitsarr_set(&x, i, 15-i);
for(int i = 0; i<16; i++)
printf("%dn", bitsarr_get(&x, i));
bitsarr_delete(x);
}
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 命名空间中具有.h和.cpp文件的类
- 从父命名空间重载类型
- 当在同一名称空间中有两个具有相同签名的函数时,会发生什么
- 在命名空间中定义函数还是限定函数
- C++:对不存在的命名空间使用命名空间指令
- 通过继承类使用来自不同命名空间的运算符
- 使用命名空间时出现多个定义错误
- OpenGL相机和相机空间转型的困惑
- CUDA内核和数学函数的显式命名空间
- 打印第二列时的2d字符矢量打印空间
- 嵌套的匿名命名空间
- CMakeLists.txt中的命名空间表示法
- 类是C++中的命名空间吗
- 在命名空间中使用全局命名空间中的函数
- 如何使 std::sort 在 std::swap 和我的命名空间的模板化交换之间没有名称冲突?
- '使用命名空间{嵌套在另一个命名空间中的某个命名空间}"
- 为什么 openmp 的并行不适用于矢量化色彩空间转换?
- 是否可以将函数导入命名空间,但不能导出它?
- 用于存储占用1位空间的位的C hack