35

I am curious to know, Is it possible to use array of bit fields? Like:

struct st
{
  unsigned int i[5]: 4;
}; 
2
  • 4
    Notice that most recent C code don't use bit fields anymore. A useful alternative would be to use bitmask fields, and do bitwise operations on them. BTW, the compiler generates equivalent code. Commented Jan 29, 2017 at 7:39
  • 9
    @BasileStarynkevitch: I wonder where you derive this from. Using bit-fields is much less error prone than handling bitmask fields with bitwise operations. Commented Feb 4, 2017 at 22:22

6 Answers 6

28

No, you can't. Bit field can only be used with integral type variables.

C11-§6.7.2.1/5

A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type.

Alternatively you can do this

struct st
{
    unsigned int i: 4;  
} arr_st[5]; 

but its size will be 5 times the size of a struct (as mentioned in comment by @Jonathan Leffler) having 5 members each with bit field 4. So, it doesn't make much sense here.

More closely you can do this

struct st
{
    uint8_t i: 4;   // Will take only a byte
} arr_st[5]; 
Sign up to request clarification or add additional context in comments.

5 Comments

But the size of that (struct st { unsigned int i: 4; } arr_st[5]; would, most likely, be 5 times the size of an unsigned int; it would not be 20 bits long.
@JonathanLeffler; Yeah. But no other ways to declare an array of bit fields.
@haccks Thanks for helping me.
type uint8_t might not be available. struct st { unsigned char i: 4; } arr_st[5]; or just unsigned char arr[5]; seem preferable.
@IlmariKaronen; I mean to say struct arr_st will be 5 times of struct like struct stt { unsigned int i: 4; unsigned int j: 4; unsigned int k: 4; unsigned int l: 4; unsigned int m: 4; } s;
11

C does not support arrays of bit-fields, so the short answer is no.

For very large arrays, it might be worthwhile to pack values, 2 per byte, this way:

#define ARRAY_SIZE  1000000

unsigned char arr[(ARRAY_SIZE + 1) / 2];

int get_4bits(const unsigned char *arr, size_t index) {
    return arr[index >> 1] >> ((index & 1) << 2);
}

int set_4bits(unsigned char *arr, size_t index, int value) {
    arr[index >> 1] &= ~ 0x0F << ((index & 1) << 2);
    arr[index >> 1] |= (value & 0x0F) << ((index & 1) << 2);
}

Comments

7

You can write your own class for this case. For example:

template <typename T, size_t ITEM_BIT_SIZE>
class BitArrayView {
private:
    static const size_t ARRAY_ENTRY_BITS = sizeof(T) * 8;
    static const T ITEM_MASK = (~((T) 0)) >> (ARRAY_ENTRY_BITS - ITEM_BIT_SIZE);
    T* arr;
public:
    struct ItemMutator {
        BitArrayView* owner;
        size_t index;
        T operator=(T value) {
            return owner->set(index, value);
        }
        operator T() {
            return owner->get(index);
        }
    };
    const size_t bitSize;
    BitArrayView(T* arr, size_t length) : arr(arr), bitSize((length * ARRAY_ENTRY_BITS) / ITEM_BIT_SIZE) {}
    T get(size_t index) const {
        size_t bitPos = index * ITEM_BIT_SIZE;
        size_t arrIndex = bitPos / ARRAY_ENTRY_BITS;
        size_t shiftCount = bitPos % ARRAY_ENTRY_BITS;
        return (arr[arrIndex] >> shiftCount) & ITEM_MASK;
    }
    T set(size_t index, T value) {
        size_t bitPos = index * ITEM_BIT_SIZE;
        size_t arrIndex = bitPos / ARRAY_ENTRY_BITS;
        size_t shiftCount = bitPos % ARRAY_ENTRY_BITS;
        value &= ITEM_MASK; // trim
        arr[arrIndex] &= ~(ITEM_MASK << shiftCount); // clear target bits
        arr[arrIndex] |= value << shiftCount; // insert new bits 
        return value;
    }
    ItemMutator operator[](size_t index) {
        return { this, index };
    }
};

And then you may access it like a "bit field" array:

// create array of some uints
unsigned int arr[5] = { 0, 0, 0, 0, 0 };

// set BitArrayView of 3-bit entries on some part of the array 
// (two indexes starting at 1)
BitArrayView<unsigned int, 3> arrView(arr + 1, 2);

// should equal 21 now => (2 * 32) / 3
arrView.bitSize == 21;

for (unsigned int i = 0; i < arrView.bitSize; i++) {
    arrView[i] = 7; // eg.: 0b111;
}

// now arr[1] should have all bits set
// and arr[2] should have all bits set but last one unset => (2 * 32) % 3 = 1
// the remaining arr items should stay untouched

This is simple implementation which should work with unsigned backing arrays only.

Notice "the mutator trick" in operator[] ;).

Of course some other operators could be implemented, too.

EDIT (After Josh Klodnicki's comment)

It actually contained a bug, which appeared when "bit chunks" overlapped partially actual array entries. Here's a fix (but it is kind of ugly ;) ).

template <typename T, size_t ITEM_BIT_SIZE>
class BitArrayView {
private:
    static const size_t ARRAY_ENTRY_BITS = sizeof(T) * 8;
    T* arr;

public:
    struct ItemMutator {
        BitArrayView* owner;
        size_t index;
        ItemMutator& operator=(T value) {
            owner->set(index, value);
            return *this;
        }
        operator T() {
            return owner->get(index);
        }
    };

    const size_t bitSize;
    BitArrayView(T* arr, size_t length) : arr(arr), bitSize((length* ARRAY_ENTRY_BITS) / ITEM_BIT_SIZE) {}

    T get(size_t index) const {
        T value = 0;
        int pos = 0;
        size_t bitPos = index * ITEM_BIT_SIZE;
        size_t arrIndex = bitPos / ARRAY_ENTRY_BITS;
        size_t readPos = bitPos % ARRAY_ENTRY_BITS;
        size_t bitCount = ITEM_BIT_SIZE;
        if (readPos > 0) {
            int chunkLength = ARRAY_ENTRY_BITS - readPos;
            if (bitCount < chunkLength) {
                value = (((arr[arrIndex] >> readPos) & ((1u << bitCount) - 1)));
                readPos += bitCount;
                return value;
            }
            value = (((arr[arrIndex] >> readPos) & ((1u << chunkLength) - 1)));
            ++arrIndex;
            readPos = 0;
            pos = chunkLength;
            bitCount -= chunkLength;
        }
        while (bitCount >= ARRAY_ENTRY_BITS) {
            value |= (arr[arrIndex++] << pos);
            pos += ARRAY_ENTRY_BITS;
            bitCount -= ARRAY_ENTRY_BITS;
        }
        if (bitCount > 0) {
            value |= ((arr[arrIndex] & ((1u << bitCount) - 1)) << pos);
        }
        return value;
    }

    void set(size_t index, T value) {
        size_t bitPos = index * ITEM_BIT_SIZE;
        size_t arrIndex = bitPos / ARRAY_ENTRY_BITS;
        size_t writePos = bitPos % ARRAY_ENTRY_BITS;
        size_t bitCount = ITEM_BIT_SIZE;

        if (writePos > 0) {
            int chunkLength = ARRAY_ENTRY_BITS - writePos;
            if (bitCount < chunkLength) {
                auto mask = (((1u << bitCount) - 1) << writePos);
                arr[arrIndex] = (arr[arrIndex] ^ ((arr[arrIndex] ^ (value << writePos)) & mask));
                writePos += bitCount;
                return;
            }
            auto mask = (((1u << chunkLength) - 1) << writePos);
            arr[arrIndex] = (arr[arrIndex] ^ ((arr[arrIndex] ^ (value << writePos)) & mask));
            ++arrIndex;
            writePos = 0;
            value >>= chunkLength;
            bitCount -= chunkLength;
        }
        while (bitCount >= ARRAY_ENTRY_BITS) {
            arr[arrIndex++] = value;
            value >>= ARRAY_ENTRY_BITS;
            bitCount -= ARRAY_ENTRY_BITS;
        }
        if (bitCount > 0) {
            auto mask = ((1u << bitCount) - 1);
            arr[arrIndex] = (arr[arrIndex] ^ ((arr[arrIndex] ^ value) & mask));
        }
    }

    ItemMutator operator[](size_t index) {
        return { this, index };
    }
};

As it is now not simple any more, below I put a simple implementation of Bit Array which access single bit separately.

template<size_t SIZE>
class BitArray {
private:
    static constexpr const size_t CELL_SIZE = sizeof(unsigned) * 8;
    static constexpr const size_t ARR_SIZE = (SIZE + CELL_SIZE) / CELL_SIZE;
    unsigned arr[ARR_SIZE];
public:
    class BitMutator {
    private:
        unsigned& cellRef;
        const size_t bitPos;

    public:
        BitMutator(unsigned& cellRef, size_t bitPos) : cellRef(cellRef), bitPos(bitPos) {}
        BitMutator(const BitMutator& source) = default;
        BitMutator(BitMutator&& source) = default;

        BitMutator& operator=(unsigned value) {
            cellRef &= ~(1u << bitPos);
            cellRef |= ((value & 1u) << bitPos);
            return *this;
        }
        operator unsigned() const {
            return (cellRef >> bitPos) & 1u;
        }
    };

    BitArray() : arr{0} {};

    BitMutator operator[](size_t index) {
        return BitMutator(arr[index / CELL_SIZE], index % CELL_SIZE);
    }

    size_t size() const {
        return SIZE;
    }

    size_t arr_size() const {
        return ARR_SIZE;
    }

    operator unsigned* () {
        return arr;
    }
};

void testBitArray() {
    BitArray<21> bitArr;

    for (int i = 0; i < bitArr.size(); i++) {
        bitArr[i] = i % 2;
    }

    for (int i = 0; i < bitArr.size(); i++) {
        std::cout << bitArr[i] << ", ";
    }

    unsigned* backing_array = (unsigned*) bitArr;
    size_t backing_arr_size = bitArr.arr_size();

    for (int i = 0; i < backing_arr_size; i++) {
        std::cout << backing_array[i] << ", ";
    }
} 

3 Comments

This implementation does not account for "fields" that overlap array entries. In the example, arrView[10] should access arr[1]{30-31} and arr[2]{0}, but as written it does not.
@JoshKlodnicki you're right, I'll fix it and edit the answer.
@JoshKlodnicki I provided a fixed version, and a simple BItArray implementation as well (as the original BitArrayView occured to appear overcomplicated after my changes).
0

No, bitfields only support integral types. But for very small arrays, you can store each element as a property individually, for example:

struct st
{
  unsigned int i0: 1;
  unsigned int i1: 1;
  unsigned int i2: 1;
  unsigned int i3: 1;
  unsigned int i4: 1;
};

The disadvantage of this approach is obviously that you can no longer use array-based operations or methods, such as run-time indexing, but it works well enough for basic applications like mathematical vectors.

Comments

0

I don't know if this is what you're after, but you can do something like this.

typedef unsigned long long ui64;
template<int BW> struct tblcnt{
  tblcnt() : m(0) {}
  ui64 operator[](int a){ return (m>>kArr[a]) & kMsk; }
  const ui64 kMsk = ((1<<BW)-1), kArr[16] = {0,BW,2*BW,3*BW,4*BW,5*BW,6*BW,7*BW,8*BW,9*BW,10*BW,11*BW,12*BW,13*BW,14*BW,15*BW};
  void operator++(){m++;}
  ui64 m;
};

Godbolt tells me it manages to generate a shift and and with constants. Now this code [] will only give us a getter. You can complement it with a setter coded in the similar vein. And with inverted shifted mask, oring with shifted value. We can define these things similarly in terms of template parameter BW so the compiler can generate optimal assembly code.

If you use one of the later C++ standards you can use array of int as template parameter and define kArr accordingly. {0,BW[0],BW[0]+BW[1],...}. Still you can get it to generate optimal code.

Even if you don't use later C++ standards (C++11 and later) you can use "pythonic":s approach in the answer here : Pass an array as template type

Comments

0

I understand that you wish indexed bit-field mostly because you want it larger than the machine-word size in bits (namely 32 or 64).

I suggest the following solution:

  1. First, define machine-word size in bits.

    #include <limits.h> 
    #define LONG_BIT ( CHAR_BIT * sizeof( long ) )
    
  2. Second, define your enumeration and number of elements at the end:

    enum { ..., ELEMS };
    
  3. Third, define size of a set that holds all elements:

    #define SET_SIZE ( ELEMS + LONG_BIT - 1 ) / LONG_BIT
    
  4. Fourth, define array type to store 32 or 64 bit long values (you can also use struct with bit-fields here, preferably that sums to machine-word bits for space optimization):

    typedef long set_t[ SET_SIZE ];
    
  5. Now you can easily access elements using following formula (and also calculate union, intersection of the sets etc. quickly).

    #define GET(set, e) ( ( set[ e / LONG_BIT ] >> ( e % LONG_BIT ) ) & 1 )
    #define SET(set, e) ( set[ e / LONG_BIT ] |= 1L << ( e % LONG_BIT ) )
    #define CLR(set, e) ( set[ e / LONG_BIT ] &= ~( 1L << ( e % LONG_BIT ) ) )
    

This applies also to bit-fields - a matter of changing set_t type definition. Actually this array set can contain any structure, not only bits, but bits allows quick set operations. Hope, that it's useful.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.