4

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?

timrau
  • 22,578
  • 4
  • 51
  • 64
tdenniston
  • 3,389
  • 2
  • 21
  • 29
  • 1
    Just out of curiosity, why do you care whether it fits in 32 bits? You plan to have hundreds of millions of these? – Nemo Oct 13 '11 at 14:10
  • @Nemo : Yes, there will be a great number of these. I believe for one of the scenarios, we will need to hold approximately 33 million of these in memory (and memory is not unlimited). However, based on the answer already given by Didier Trosset, I am ok relaxing the constraint to 5 bytes, instead of 4 bytes. – tdenniston Oct 13 '11 at 14:14
  • 2
    Beware that by having your structure on 5 bytes, and using packed values on more that one byte (the ones with 14, 16, or 30 bits) you can get penalty to move memory to a 32 bits register from a NON-32 bits aligned memory address. It is not *just* a matter of one more byte. – Didier Trosset Oct 13 '11 at 14:21
  • @DidierTrosset: Hmm. That is a good point I did not consider. I wonder then if the correct approach for me is to pack things into a `uint32_t` and manipulate the contents via bit fiddling. I did like your answer you posted, except for the duplication of the `state` field. – tdenniston Oct 13 '11 at 14:33
  • You can keep the 2 bits unused in the second struct. It is perfectly valid C. See my answer. – Didier Trosset Oct 13 '11 at 14:51
  • I just came across this and note that nobody really answered your question about bitfields vs masking. I've worked in embedded systems for many years and have in the past considered using bitfields to represent hardware registers. I never did though and am curious how it turned out for you. – William Morris Nov 21 '12 at 12:38

1 Answers1

4

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;
      };
   };
};
Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
  • Thanks for making this point. However, I believe it's ok if I relax my constraint to make 5 bytes available, so the union alignment is ok. – tdenniston Oct 13 '11 at 14:16