Will this bitfield work the way I expect?

2019-05-07 00:14发布

问题:

I've been doing some reading about bitfields in C, how the C standard doesn't enforce any particular ordering of the fields in a machine word, and so on.

I hope this question appropriately fits the format of SO.

My question is whether my struct (definition following) will actually perform in the way I expect. Here's the definition I came up with, and then I'll discuss what I want:

typedef enum {
   STATE_ONE,
   STATE_TWO,
   STATE_THREE,
   STATE_FOUR
} __attribute__ ((packed)) State;

typedef struct MyStruct {
   // One of State enum (maximum of 4 states).
   unsigned state : 2;

   // Remaining 30 bits are used differently depending on 'state'.
   union {
      // If 'state' is STATE_ONE (0), the remaining bits are an ID number.
      unsigned id : 30;

      /* 
       * For all other states, the remaining bits are the 2-tuple:
       *  (buffer A index, buffer B index).
       */
      struct {
         unsigned bufferAIdx : 16;
         unsigned bufferBIdx : 14;
      } __attribute__ ((packed)) index;
   } __attribute__ ((packed));
} __attribute__ ((packed)) MyStruct;

(This is for gcc, thus the __attribute__ directives).

You can probably tell what I'm going for: depending on the value of the 'state' field I want to use the remaining 30 bits for different purposes. They should either be an ID number, or a 2-tuple of indices into various buffers. And, each instance of MyStruct should fit into a maximum of 5 bytes.

So what I want to be able to do is something to this effect:

MyStruct a, b;
a.state = STATE_ONE;
a.id = 123456;

b.state = STATE_THREE;
b.index.bufferAIdx = 6;
b.index.bufferBIdx = 2;

Mainly I'm looking for input on whether this is "the right thing" to do. In other words, am I misusing the ideas of bitfields/unions here? If you were going to be a maintainer of this code, would you cringe in horror upon seeing this? Or, would you prefer to see the whole data object stored in a uint32_t type and manipulated via masking and shifting?

回答1:

As the beginning of any union or struct will be aligned on a boundary, you cannot fit all your data in 32 bits this way. You should encapslulate your union and struct the other way round, as follows (attributes removed for readability):

typedef struct MyStruct {
   union {
      struct {
         unsigned state :  2;
         unsigned id    : 30;
      }
      struct {
         unsigned /* unused */ :  2; 
         unsigned bufferAIdx   : 16;
         unsigned bufferBIdx   : 14;
      };
   };
};