0

I'm learning about bit flags and creating bit fields manually using bit-wise operators. I then came across bitsets, seemingly an easier and cleaner way of storing a field of bits. I understand the value of using bit fields as far as minimizing memory usage. After testing the sizeof(bitset), though, I'm having a hard time understanding how this is a better approach.

Consider:

#include <bitset>
#include <iostream>

int main ()
{

    // Using Bit Set, Size = 8 Bytes
    const unsigned int i1 = 0;
    const unsigned int i2 = 1;

    std::bitset<8> mySet(0);

    mySet.set(i1);
    mySet.set(i2);

    std::cout << sizeof(mySet) << std::endl;

    // Manually managing bit flags

    const unsigned char t1 = 1 << 0;
    const unsigned char t2 = 1 << 1;

    unsigned char bitField = 0;
    bitField |= t1 | t2;

    std::cout << sizeof(bitField) << std::endl;

    return 0;
}

The output is:

The mySet is 8 bytes. The bitField is 1 byte.

Should I not use std::bitset if minimal memory usage is desired?

Logan West
  • 45
  • 6
  • 1
    `bitset` has conversions to and from `unsigned long long`, making it not unreasonable to have that type stored internally. The type is also a lot more convenient if you need `bitset<1000>`. If you just need a few bits, an unsigned char might work better. – Bo Persson May 25 '18 at 18:25
  • Bo, are you saying bitset pays off when working with a very large set of bits to manage? But for minimizing memory usage, I should stick with manual bit shifting? – Logan West May 25 '18 at 18:44
  • If you use very few bits and minimal space is more important than the convenience of a ready made class, then yes. Some compilers might optimize the size of a small `bitset`, but not everyone. – Bo Persson May 25 '18 at 18:47
  • @LoganWest -- Well, you really couldn't expect the size of a class with members to be a single byte. – PaulMcKenzie May 25 '18 at 18:48

1 Answers1

1

For the lowest possible memory footprint you shouldn't use std::bitset. It will likely require more memory than a plain built in type like char or int of equivalent effective size. Thus, it probably has memory overhead, but how much will depend on the implementation.

One major advantage of std::bitset is that it frees you from hardware-dependent implementations of various types. In theory, the hardware can use any representation for any type, as long as it fulfills some requirements in the C++ standard. Thus when you rely on unsigned char t1 = 1 to be 00000001 in memory, this is not actually guaranteed. But if you create a bitset and initialize it properly, it won't give you any nasty surprises.

A sidenote on bitfiddling: considering the pitfalls to fiddling with bits in this way, can you really justify this error-prone method instead of using std::bitset or even types like int and bool? Unless you're extremely resource constrained (e.g. MCU / DSP programming), I don't think you can.

Those who play with bits will be bitten, and those who play with bytes will be bytten.


By the way, the char bitField you declare and manipulate using bitwise operators is a bit field, but it's not the C++ language notion of a bit field, which looks like this:

struct BitField{
    unsigned char flag1 : 1, flag2 : 1, flag3 : 1;
}

Loosely speaking, it is a data structure whose data member(s) are subdivided into separate variables. In this case the unsigned char of (presumably) 8 bits is used to create three 1-bit variables (flag1, flag2 and flag3). It is explicitly subdivided, but at the end of the day this is just compiler-/language-assisted bit fiddling similar to what you did above. You can read more about bit fields here.