用于存储占用1位空间的位的C hack

C hack for storing a bit that takes 1 bit space?

本文关键字:hack 空间 1位 存储 用于      更新时间:2023-10-16

我有一长串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 / 32uint32_ti % 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);
}