9

Possible Duplicate:
Converting Bit Field to int

I am working on an application, part of which handles 16-bit words that contain a number of 1-bit flags. I am handling the data using a structure similar to the one shown below:

struct mystruct
{
   uint16_t Reserved1   :3;
   uint16_t WordErr     :1;
   uint16_t SyncErr     :1;
   uint16_t WordCntErr  :1;
   uint16_t Reserved2   :10;
};

i.e. the structure contains a single 16-bit variable that is handled as a number of smaller (in some cases, 1-bit flag) pieces.

My question is this, is there a simple way to handle the entire 16-bit word as one value, say, to output it to the console, or a file, or add it to another data structure? I don't know of any way of doing this besides shifting the individual structure elements and adding them to a temporary uint16_t variable. It seems that there is probably a simpler way of extracting the entire word, but I can't find any information on how the compiler handles a structure like this.

EDIT: I suppose this may be obvious but what I am trying to do in a nutshell is be able to access the 1-bit flags individually, as well as use the structure as a single variable of type uint16_t (i.e. unsigned short, 16 bits).

Community
  • 1
  • 1
Joel M.
  • 248
  • 1
  • 2
  • 12
  • It doesn't necessarily hold a 16-bit variable -- there can be padding between the fields. The actual layout is implementation-defined. And note that `uint16_t` is not required to exist; better to use `uint_least16_t`. – Pete Becker Sep 18 '12 at 18:32

4 Answers4

12

The standard approach here is to use anonymous structs/unions, like this:

union mystruct
{
   struct
   {
      uint16_t Reserved1   :3;
      uint16_t WordErr     :1;
      uint16_t SyncErr     :1;
      uint16_t WordCntErr  :1;
      uint16_t Reserved2   :10;
   };

   uint16_t word_field;
};

or, if union is not good as a top level object,

struct mystruct
{
   union
   {
       struct
       {
          uint16_t Reserved1   :3;
          uint16_t WordErr     :1;
          uint16_t SyncErr     :1;
          uint16_t WordCntErr  :1;
          uint16_t Reserved2   :10;
       };

       uint16_t word_field;
   };
};

This definition allows direct access to the inner fields, like:

mystruct s1;
s1.WordCntErr = 1;

Strictly speaking, compiler is not giving any guarantees on how different members of the union will overlap each other. It can use different alignments and even shifts. A lot of people here will readily point this out. Nevertheless, looking at this from the practical standpoint, if all fields of the union have the same size you can safely assume that they occupy the same piece of memory. For example, the code

s1.word_field = 0;

will zero out all bit fields. Tons of code are using this. It is unthinkable that this will ever stop working.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • 5
    And this produces undefined behavior if you write to one field of the union and then read from the other. – Pete Becker Sep 18 '12 at 19:13
  • So, is the union "uniting" the contents of the struct into the variable uint16_t word_field? I don't see how the struct and word_field are connected, except that they are both members of the union. – Joel M. Sep 18 '12 at 20:27
  • 1
    A union results in all members starting at the same place in memory. So here word_field and Reserved1 start at the same address. Note that Pete is correct, though. Setting data through the struct and then reading through word_field (or vice versa) results in undefined behavior. – Bill Sep 18 '12 at 22:13
  • Union is placing all its members one on top of another regardless of the type of the members. For example `bool` can overlap `double`, any class/struct, enum, whatever else. Members of the union are connected only by the fact that they are members of the same union. I aslo updated the answer. – Kirill Kobelev Sep 19 '12 at 01:52
4

The short answer is you can't do it. The longer answer is that you can do it, but the details depend on your compiler. This particular bit-field layout looks suspiciously like it's supposed to map to a hardware register, in which case you've already got compiler dependencies: the details of how the bit-fields are arranged is implementation-defined. So while you're assuring yourself that the compiler lays them out the way you expect, you can also check whether it supports type puns through a union. Although writing to one field of a union and reading from another formally produces undefined behavior, both in C and in C++, most (all?) compilers support it in simple cases like this.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
2

An alternative to the undefined behavior that comes from the union technique, you could copy the data:

mystruct m;
m.Reserved1 = 0;
m.WordErr = 1;
m.SyncErr = 0;
m.WordCntErr = 0;
m.Reserved2 = 0;

uint16_t value = 0;
memcpy(&value, &m, sizeof(value));

[Code]

Of course, the output is platform-specific / endian-sensitive, so if you plan on writing it out so you can read it in again then take that into account.

Bill
  • 14,257
  • 4
  • 43
  • 55
1

That's what a union is for. I hardly ever need to use one, so my syntax may be rusty, but it looks something like this:

union myunion
{
    struct mystruct
    {
       uint16_t Reserved1   :3;
       uint16_t WordErr     :1;
       uint16_t SyncErr     :1;
       uint16_t WordCntErr  :1;
       uint16_t Reserved2   :10;
    };
    uint16_t word;
};

Of course, that adds typing whenever you access it, so you might want to just try a typecast if you only need it occasionally.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
  • 7
    Wouldn't that be undefined behaviour the moment you do what I think you're about to do? – Kerrek SB Sep 18 '12 at 18:34
  • 2
    The syntax is correct, but accessing a field of a union that hasn't been initialized produces undefined behavior. In fact, every compiler in existence lets you stuff values into the bit fields and access the whole through `word`, but formally, it's undefined behavior. This is the classic C type pun, but it's **not** required to work (not in C nor in C++). – Pete Becker Sep 18 '12 at 18:35
  • C++ provides one exception to the undefined-ness in that a union of two structs with the same initial fields it is OK to access any of those fields. E.g. `union{struct{int a;},struct{int b;}}` – Dave Rager Sep 18 '12 at 18:39
  • 1
    @DaveRager - yes, that comes from the analogous property of structs: if two structs have the same sequence of initial element types, you can access any of those initial elements through a pointer to the other type. But that doesn't help here... – Pete Becker Sep 18 '12 at 18:43
  • @PeteBecker right it doesn't directly help here but I frequently run into code where a union is being used incorrectly in this way. I thought it might be helpful to point out the one instance where it is correct. – Dave Rager Sep 18 '12 at 18:59
  • can you give an example of what you mean by "try a typecast"? – Joel M. Sep 18 '12 at 19:27
  • @PeteBecker It's not undefined behaviour per se in C11, footnote 95 in 6.5.2.3 says "If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation." As far as I remember, it's already been added in TC2 to C99. – Daniel Fischer Sep 18 '12 at 19:28
  • @DanielFischer - thanks for pointing that out. A note of caution: footnotes are not normative; formally, they give guidance on how to interpret things, but they do not impose requirements. Here, of course, the footnote pretty clearly spells out the intention. Still, the result "might be a trap value", which applies to a case like this, where the bit-field part of the union has uninitialized values; that means undefined behavior. – Pete Becker Sep 18 '12 at 19:40
  • @PeteBecker Right, if you get a trap representation, it's UB. But `uintN_t`, if it exists, can't have any trap representations, so you just get an unspecified value if not all bit-fields have been initialised. – Daniel Fischer Sep 18 '12 at 20:05