19

Would anyone know how to extract the size of a bit-field member. The below code naturally gives me the size of an integer, but how do I find out how many bits or bytes are in mybits.one? I've tried sizeof(test.one) but which clearly won't work. I realize this is a measure of bits:

#include <iostream>

using namespace std;

int main()
{
    struct mybits {
        unsigned int one:15;
    };

    mybits test;
    test.one = 455;
    cout << test.one << endl;
    cout << "The size of test.one is:  " << sizeof(test) << endl;
}
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
domonica
  • 526
  • 7
  • 14
  • 1
    Runtime solution: set the value of the whole structure to zero, then inverse the field using “~”, and then count the number of bits set to “1”. http://social.msdn.microsoft.com/Forums/en-US/7e4f01b6-2e93-4acc-ac6a-b994702e7b66/finding-size-of-bitfield – Alex F Nov 25 '13 at 13:22

6 Answers6

7

The draft C++ standard says sizeof shall not be applied to a bit-field in section 5.3.3 Sizeof paragraph 1. If you have control of the source then using an enum sounds much simpler and neater:

struct mybits
{
    enum bitFieldSizes
    {
        field1 = 15,
        field2 = 2,
        field3 = 4,
        field4 = 8,
        field5 = 31
    };

    unsigned int one : field1 ;  
    unsigned int two : field2 ;  
    unsigned int three : field3 ;
    unsigned int four : field4 ;
    unsigned int five : field5 ;
};

If you don't have control of the source it is possible to use bit hacks to obtain the size of your bit-field and std::bitset makes it easier:

#include <iostream>
#include <bitset>

struct mybits
{
    unsigned int one : 15 ;  
    unsigned int two : 2 ;  
    unsigned int three : 4 ;
    unsigned int four : 8 ;
    unsigned int five : 31 ;
};

int main()
{
    mybits mb1 ;

    mb1.one   =  ~0 ;
    mb1.two   =  ~0 ;
    mb1.three =  ~0 ;
    mb1.four  =  ~0 ;
    mb1.five  =  ~0 ;

    std::bitset<sizeof(unsigned int)*8> b1(mb1.one);
    std::bitset<sizeof(unsigned int)*8> b2(mb1.two);
    std::bitset<sizeof(unsigned int)*8> b3(mb1.three);
    std::bitset<sizeof(unsigned int)*8> b4(mb1.four);
    std::bitset<sizeof(unsigned int)*8> b5(mb1.five);

    std::cout << b1 << ":" << b1.count() << std::endl ;
    std::cout << b2 << ":" << b2.count() << std::endl ;
    std::cout << b3 << ":" << b3.count() << std::endl ;
    std::cout << b4 << ":" << b4.count() << std::endl ;
    std::cout << b5 << ":" << b5.count() << std::endl ;
}

which produces the following output:

00000000000000000111111111111111:15
00000000000000000000000000000011:2
00000000000000000000000000001111:4
00000000000000000000000011111111:8
01111111111111111111111111111111:31
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
4

Runtime solution, the idea from this discussion: http://social.msdn.microsoft.com/Forums/en-US/7e4f01b6-2e93-4acc-ac6a-b994702e7b66/finding-size-of-bitfield

#include <iostream>
using namespace std;

int BitCount(unsigned int value)
{
    int result = 0;

    while(value)
    {
        value &= (value - 1);
        ++result;
    }

    return result;
}

int main()
{
    struct mybits {
        unsigned int one:15;
    };

    mybits test;
    test.one = ~0;

    cout << BitCount(test.one) << endl;

    return 0;
}

Prints 15.

Alex F
  • 42,307
  • 41
  • 144
  • 212
  • 2
    not sure, but this may depend on the compiler, since it is undefined how the padded bits are set. They may contain garbage. So, watch out how you use this. Besides it is done at run-time. – BЈовић Nov 25 '13 at 15:11
  • @BЈовић: only `test.one` is passed to `BitCount`, it should not contain padded bits. It is possible to zero the whole structure first, but I don't think it is necessary. – Alex F Nov 25 '13 at 15:14
4

A compile-time solution using constexpr:

struct S
{
    unsigned int a : 4;
    unsigned int b : 28;
};

#define GET_BIT_FIELD_WIDTH(T, f) \
    []() constexpr -> unsigned int \
    { \
        T t{}; \
        t.f = ~0; \
        unsigned int bitCount = 0; \
        while (t.f != 0) \
        { \
            t.f >>= 1; \
            ++bitCount; \
        } \
        return bitCount; \
    }()

int main()
{
    constexpr auto a = GET_BIT_FIELD_WIDTH(S, a);
    constexpr auto b = GET_BIT_FIELD_WIDTH(S, b);
    static_assert(a == 4);
    static_assert(b == 28);
}

I think it does not invoke any undefined behavior, but it does invoke some implementation-defined behavior:

  1. Wrap-around of bit-fields is implementation-defined.
  2. Above solution will not work for signed fields if right-shift of signed fields uses sign-extension (implemented-defined). The compiler will hit an infinite loop in that case.
TripShock
  • 4,081
  • 5
  • 30
  • 39
  • 1
    Great solution, thanks. With gcc, I get an overflow warning due to trying to assign the ~0 to the narrower bit field. `warning: conversion from 'unsigned int' to 'unsigned char:4' changes value from '4294967295' to '15' [-Woverflow]` With clang it seems OK. Instead, setting t.f = 1, and then left shifting until the bit falls off the left side seems to work well. `t.f = 1; while (t.f != 0) { t.f <<= 1; ...` or `for (t.f = 1; t.f !=0; t.f <<= 1, bitcount ++);` – Jack Culhane Jan 27 '22 at 09:42
  • This is cool and Jack's comment resolves the calculation of signed fields without the need to determine an [unsigned version of T](https://en.cppreference.com/w/cpp/types/make_unsigned) (which is probably not possible with bit fields). – Alexis Wilke Jun 19 '22 at 21:12
2

Because of padding it is not possible to see number of bits in a bit field using sizeof operator.

The only way is to open up the header where the structure is defined, and look it up.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Ok, thanks. But if I change the type to char and exceed 8 bits in the size of the bitfield member then I can use the sizeof function to determine the rounded up number of bytes. The compiler gives me a warning about exceeding the size of char 8 bits in a bitfield. Is it still ok to do it? Will it cause issues later? – domonica Nov 25 '13 at 13:26
  • @domonica First of all, I really hope you are not ignoring compiler warnings, as it is the thing sloppy people do. Not sure what will happen. It may be an undefined or implementation-defined behavior. In any case, I wouldn't do it – BЈовић Nov 25 '13 at 15:08
  • Thanks everyone, some really useful points here. And,, I promise I won't ignore compiler warnings.. – domonica Nov 26 '13 at 00:57
2

There is no way to get this information (apart from reading the declaration yourself). As per the standard, [C++11]expr.sizeof§1, it's illegal to call sizeof on a bit-field:

The sizeof operator shall not be applied to ... an lvalue that designates a bit-field.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
-2

Here is a little bit tricky generalized version:

#include <iostream>
#include <limits>
#include <bitset>
#include <cstring>
using namespace std;

template <class T>
T umaxof()
{
      T t;
      memset(&t, 0xFF, sizeof(T));
      return t;
}

template <class T>
size_t bitsof(const T& umax)
{
    return bitset<sizeof(T)*8>(umax).count();
}

int main() 
{
    struct A
    {
        uint32_t bf1:19;
        uint32_t bf2:1;
    };

    cout << bitsof(umaxof<A>().bf1) << "\n";
    cout << bitsof(umaxof<A>().bf2) << "\n";

    return 0;
}

It can be tried out at https://ideone.com/v4BiBH

Note: Works with unsigned bit fields only.

em2er
  • 811
  • 5
  • 15