在c ++中操作位数组中的字段

时间:2009-03-17 19:56:05

标签: c++ stl bit-manipulation

所以我想知道设置和操作位域。

我已经找到了C/C++ Code to treat a character array as a bitstream,这与我的问题类似,但它并没有给我一个很好的stl方法,我认为必须存在。

我在考虑来自stl的位集,但我的数据集非常复杂,位格式为1,2,3,4,7,8,16位格式。

让我们说我想访问我的数据中的一个项目,第4个字段是一个跨越字节边界的8位代码段,是否有一种简单的方法可以做到这一点?

byte 1   byte 2
11112344 44444455

我正在寻找一个好的stl实现来访问4中的数据或者将数据设置为4,我假设存在一些东西,因为将数据转换为单个字节并将其屏蔽掉似乎很愚蠢。写它似乎也很困难,并且应该有一种更简单的方法来完成这样的任务。

还有其他办法吗?

编辑 - 我的数据集长度大约为20个字节,我希望按位顺序保留所有数据

3 个答案:

答案 0 :(得分:1)

你能解释为什么常规位域不足吗?换句话说,为什么不这样做:

    struct ComplexBitLayout {
      unsigned field1 :4;
      unsigned field2 :1;
      unsigned field3 :1;
      unsigned field4 :8;
      unsigned field5 :2;
   } __attribute__((__packed__)); // or your compiler's equivalent

   ComplexBitLayout cbl;

   cbl.field4 = x;

做你想做的事吗?

您是否希望能够以编程方式动态构建不同的布局?

答案 1 :(得分:1)

无论你添加什么语法糖,都会发生掩蔽和转移。如果你想让事情变得非常容易使用,但是首先要做的有点困难,你可以使用一个类,以及一些宏/模板代码来使定义新类更容易:

template<bool> struct CompileTimeAssert;
template<> struct CompileTimeAssert<true> { };

#define ASSERT(check) if (!check) throw exception("Assertion Failure" #check)

#define ADDBITVALUE(backingField, backingFieldType, fieldName, offset, size) \
    public: \
    static const unsigned int fieldName##Offset = offset; \
    static const backingFieldType fieldName##Mask = CalculateMask<offset, size, backingFieldType>::Value; \
    public: \
    void Set##fieldName(backingFieldType value) \
    {\
        ASSERT(("Value too large for field.", (value & (fieldName##Mask >> fieldName##Offset)) == value));\
        backingField |= value << fieldName##Offset;\
    }\
    backingFieldType Get##fieldName() const\
    {\
        return (backingField & fieldName##Mask) >> fieldName##Offset;\
    }\
    private:

#define ADDSPANNEDVALUE(backingField1, backingField1Type, backingField2, backingField2Type, fieldName, offset1, size1, offset2, size2)\
    ADDBITVALUE(backingField1, backingField1Type, fieldName##internal1, offset1, size1)\
    ADDBITVALUE(backingField2, backingField2Type, fieldName##internal2, offset2, size2)\
    public: \
    void Set##fieldName(backingField1Type value) \
    {\
        backingField1Type value1 = value << (sizeof(backingField1Type)*8-size1);\
        value1 = value1 >> (sizeof(backingField1Type)*8-size1);\
        Set##fieldName##internal1(value1);\
        Set##fieldName##internal2(value >> size1);\
    }\
    backingField1Type Get##fieldName() const\
    {\
        return Get##fieldName##internal1() | (Get##fieldName##internal2() << size1);\
    }\
    private:

template <unsigned int Offset, int Size, typename T>
struct CalculateMask
{
    CompileTimeAssert<(Size > 0)> Object;
    static const T Value = (T)((1 << Offset) | CalculateMask<Offset + 1, Size - 1, T>::Value);
};

template <unsigned int Offset, typename T>
struct CalculateMask<Offset, 0, T>
{
    CompileTimeAssert<(Offset <= sizeof(T) * 8)> Object;
    static const T Value = 0;
};

然后像这样定义你的类:

class BitGroup
{
    unsigned short Values;
    unsigned short Values2;
    ADDBITVALUE(Values, unsigned short, Field1, 0, 12);
    ADDSPANNEDVALUE(Values, unsigned short, Values2, unsigned short, Field2, 12, 4, 0, 2);
    ADDBITVALUE(Values2, unsigned short, Field3, 2, 14);
public:
    BitGroup() : Values(0), Values2(0) {}
};

用法:

BitGroup bg;
bg.SetField1(15);
cout << bg.GetField1();
bg.SetField2(63);
cout << bg.GetField1();

如果您的字段超出了支持字段的范围,您将获得编译时断言。没有检查字段是否重叠,因此您必须注意这一点。

答案 2 :(得分:0)

现有的类vector<bool>Boost.DynamicBitset似乎不会为您做任何事情。

底层实现将不得不进行转换和屏蔽,对此没有任何愚蠢。使用基础vector<bool>vector<something_else>编写自己的类或模板并不难,然后您可以根据您的需求优化移位/屏蔽代码,例如:

  • 这是随机访问还是顺序? (如果它是顺序的,那么你可以避免重新计算你的移位掩码。)
  • 所有元素都是相同的大小,还是在任意位偏移量下索引任意大小的元素?