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

时间:2013-03-18 18:16:45

标签: c++ c arrays binary bit-manipulation

我在0到67600之间有一长串数字。现在我想使用长度为67600个元素的数组存储它们。如果数字在集合中,则元素设置为1;如果数字不在集合中,则设置为0。即。每次我只需要1bit信息来存储一个数字。 C / C ++有没有帮助我实现这个目标?

5 个答案:

答案 0 :(得分:15)

在C ++中,如果大小是动态的,则可以使用std::vector<bool>(这是std::vector的特殊情况,请参阅this),否则会std::bitset(首选{{1}如果可能的话。)如果你需要在运行时设置/更改大小,还有std::bitset。你可以找到它的信息here,这很酷!

在C(和C ++)中,您可以使用按位运算符手动实现它。常见操作的一个很好的总结是here。我想提到的一件事是,在进行位操作时使用无符号整数是一个好主意。在移动负整数时,boost::dynamic_bitset<<未定义。您需要分配某些整数类型的数组,如>>。如果您要存储uint32_t位,则需要NN/32。位uint32_t存储在i'i % 32的{​​{1}}位。您可能希望使用不同大小的整数类型,具体取决于您的体系结构和其他约束。 注意:更喜欢使用现有的实现(例如,在C ++的第一段中描述,搜索Google的C解决方案)而不是滚动自己(除非您特别想要,在这种情况下我建议了解更多关于在解决这个问题之前,来自其他地方的二进制/位操作。)这种事情已经完成了死亡并且有“好的”解决方案。

有许多技巧可能只消耗一位:例如位域数组(也适用于C),但是否使用的空间是否由编译器决定。请参阅this link

请注意,无论您做什么,您几乎肯定无法使用完全 N位来存储N位信息 - 您的计算机很可能无法分配少于8位:你想要7位,你将不得不浪费1位,如果你想要9位,你将需要16位并浪费其中的7位。即使您的计算机(CPU + RAM等)可以在单个位上“操作”,如果您在具有i / 32 / uint32_t的操作系统中运行,您的分配器也无法跟踪数据由于开销导致如此小的精度。最后一个资格非常愚蠢 - 你不会发现一个正在使用的架构允许你在我想象的时间内以低于8位的速度运行:)

答案 1 :(得分:10)

您应该使用std::bitset

std::bitset函数类似于bool的数组(实际上类似std::array,因为它按值复制),但每个元素只使用1位存储空间。

另一个选项是vector<bool>,我不建议这样做,因为:

  • 它使用较慢的指针间接和堆内存来启用调整大小,这是您不需要的。
  • 该类型经常受到标准纯粹主义者的诽谤,因为它声称是标准容器,但未能遵守标准容器的定义*。

*例如,符合标准的函数可能期望&container.front()生成指向任何容器类型的第一个元素的指针,该指针因std::vector<bool>而失败。 也许是您的使用案例的挑剔,但仍然值得了解。

答案 2 :(得分:6)

实际上有! std::vector<bool>对此有专长:http://en.cppreference.com/w/cpp/container/vector_bool

请参阅文档,它尽可能高效地存储它。

编辑:正如其他人所说,std::bitset也可用:http://en.cppreference.com/w/cpp/utility/bitset

答案 3 :(得分:4)

如果要用C语言编写,请使用长度为67601位的char数组(67601/8 = 8451),然后为每个值打开/关闭相应的位。

答案 4 :(得分:1)

其他人给出了正确的想法。这是我自己实现的bitsarr或&#39;数组&#39;比特。无符号字符是一个字节,因此它本质上是一个无符号字符数组,用于将信息存储在各个位中。我添加了除了一位值之外还存储两个或四个位值的选项,因为它们都除以8(一个字节的大小),如果你想存储大量范围为0的整数,它会很有用。 -3或0-15。

设置和获取时,数学在函数中完成,因此你可以给它一个索引,好像它是一个普通的数组 - 它知道在哪里看。

此外,用户有责任不传递值来设置太大,否则会搞砸其他值。它可以被修改,以便溢出循环回到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("%d\n", 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("%d\n", bitsarr_get(&x, i));
    bitsarr_delete(x);
}