bitfield organization

Discussion in 'C Programming' started by David Resnick, May 27, 2010.

  1. I'm a bit confused about how bitfield organization works.

    In the following code:

    #include <stdio.h>
    #include <string.h>
    #include <inttypes.h>

    /* Layout of structure
    * 7 6 5 4 3 2 1 0
    *| PDU | Frame |
    *| FQC | RFCI |
    *| Header CRC | Pay
    * load CRC |

    i.e.
    4 bits PDU
    4 bits Frame
    2 bits FQC
    6 bits RFCI
    6 bits Header CRC
    10 bits Payload CRC
    */

    int main(void)
    {
    unsigned int i;
    typedef struct
    {
    uint8_t frame:4; // The frame number
    uint8_t PDUType:4; // The PDU type, we support 0 for media
    or 14 for ctrl
    uint8_t RFCI:6; // Radio Access Bearer Sub Flow
    Indicator (helpful?)
    uint8_t FQC:2; // Frame Quality Classification
    uint16_t HeaderCRC:6; // CRC for checking header integrity
    uint16_t PayloadCRC:10; // CRC for checking payload integrity

    } IU_UP_PDU_TYPE_0;

    IU_UP_PDU_TYPE_0 blah;

    memset(&blah, '\0', sizeof blah);

    blah.HeaderCRC = 0x3F;

    unsigned char *pBlah = (unsigned char*)&blah;
    for ( i = 0; i < sizeof blah; ++i )
    {
    printf("%02X ", (unsigned int) pBlah);
    }

    printf("\n");

    return 0;
    }

    I can't see a way to get the Header CRC to occupy the correct bits in
    the overall arrangement.

    temp(641)$ gcc -Wall -o foo foo.c
    temp(642)$ foo
    00 00 3F 00

    As you can see, it occupies the 6 LSB of the uint16_t.

    I gather bitfields aren't very portable. Any wisdom on the above?

    Thanks,
    -David
     
    David Resnick, May 27, 2010
    #1
    1. Advertising

  2. On May 27, 10:32 am, David Resnick <> wrote:
    > I'm a bit confused about how bitfield organization works.
    >
    > In the following code:
    >
    > #include <stdio.h>
    > #include <string.h>
    > #include <inttypes.h>
    >
    > /* Layout of structure
    >  * 7  6  5  4  3  2  1  0
    >  *|  PDU      | Frame   |
    >  *| FQC |     RFCI       |
    >  *| Header CRC   | Pay
    >  *  load CRC             |
    >
    >  i.e.
    >   4 bits PDU
    >   4 bits Frame
    >   2 bits FQC
    >   6 bits RFCI
    >   6 bits Header CRC
    >  10 bits Payload CRC
    >  */
    >
    > int main(void)
    > {
    >     unsigned int i;
    >     typedef struct
    >     {
    >         uint8_t frame:4;       // The frame number
    >         uint8_t PDUType:4;     // The PDU type, we support 0 for media
    > or 14 for ctrl
    >         uint8_t RFCI:6;        // Radio Access Bearer Sub Flow
    > Indicator (helpful?)
    >         uint8_t FQC:2;         // Frame Quality Classification
    >         uint16_t HeaderCRC:6;   // CRC for checking header integrity
    >         uint16_t PayloadCRC:10; // CRC for checking payload integrity
    >
    >     } IU_UP_PDU_TYPE_0;
    >
    >     IU_UP_PDU_TYPE_0 blah;
    >
    >     memset(&blah, '\0', sizeof blah);
    >
    >     blah.HeaderCRC = 0x3F;
    >
    >     unsigned char *pBlah = (unsigned char*)&blah;
    >     for ( i = 0; i < sizeof blah; ++i )
    >     {
    >         printf("%02X ", (unsigned int) pBlah);
    >     }
    >
    >     printf("\n");
    >
    >     return 0;
    >
    > }
    >
    > I can't see a way to get the Header CRC to occupy the correct bits in
    > the overall arrangement.
    >
    > temp(641)$ gcc  -Wall -o foo foo.c
    > temp(642)$ foo
    > 00 00 3F 00
    >
    > As you can see, it occupies the 6 LSB of the uint16_t.
    >
    > I gather bitfields aren't very portable.  Any wisdom on the above?
    >
    > Thanks,
    > -David


    After looking at it for a bit, doesn't seem to work to have those 2
    LSB of the 3rd byte want to be attached to the 8 bits of the 4th
    byte. So I'm doing this, seem reasonable?

    typedef struct
    {
    uint8_t frame:4; // The frame number
    uint8_t PDUType:4; // The PDU type, we support 0 for
    media or 14 for ctrl
    uint8_t RFCI:6; // Radio Access Bearer Sub Flow
    Indicator (helpful?)
    uint8_t FQC:2; // Frame Quality Classification
    uint8_t PayloadCRCMSB:2; // the 2 MSB of the CRC for checking
    payload integrity
    uint8_t HeaderCRC:6; // CRC for checking header integrity
    uint8_t PayloadCRCLSB:8; // the 8 LSB of the CRC for checking
    payload integrity

    } IU_UP_PDU_TYPE_0;
     
    David Resnick, May 27, 2010
    #2
    1. Advertising

  3. On 27 May, 15:32, David Resnick <> wrote:

    > I'm a bit confused about how bitfield organization works.


    <snip>

    > I gather bitfields aren't very portable.  Any wisdom on the above?


    bitfields aren't very portable it's a bit of a git. I couldn't work
    out from your example what you were tryting to do. If you want to use
    bitfields then you need to consult your compiler documentation (if
    there is any!) on how they're laid out in memory. Otherwise you're
    going to have to experiment.

    You might end up writing (or getting one off the internet) a bit read/
    writer library using shifts and masks. If *that's* too slow then write
    a code generator to generate the appropriate shifts and masks. A bit
    of a pain but you should only need to do it once..
     
    Nick Keighley, May 27, 2010
    #3
  4. Am 27.05.2010 16:32, schrieb David Resnick:
    >
    > I gather bitfields aren't very portable. Any wisdom on the above?


    Isn't the use of uint8_t, uint16_t in bitfields already nonportable?
    AFAIK these should be "unsigned int" (and a bit confusing, if you ever
    need a signed one you have to use "signed int", "int" won't do).

    Philipp
     
    Philipp Klaus Krause, May 27, 2010
    #4
  5. David Resnick

    Eric Sosman Guest

    On 5/27/2010 10:32 AM, David Resnick wrote:
    > I'm a bit confused about how bitfield organization works.


    Don't feel bad: It's inherently confusing, because the compiler
    has wide latitude in how it arranges things and the language imposes
    very few restrictions.

    Bit-fields reside in unnamed "addressable storage units," whose
    size and nature are entirely up to the compiler -- and the compiler
    is not even required to document how it makes its choice. Here's
    all we're guaranteed about the layout of bit-fields in ASU's:

    - The ASU containing a particular bit-field is after the bytes
    occupied by non-bit-field elements named earlier, and before
    any non-bit-field elements named later. That is, the ASU's
    are positioned as you'd expect, relative to non-bit-fields
    in the same struct.

    - If the struct contains two or more consecutive bit-fields,
    the ASU containing the earlier bit-field does not appear
    after the ASU containing a later bit-field. That is, ASU's
    are allocated to bit-fields in non-decreasing order.

    - If the struct contains two or more consecutive bit-fields,
    they'll be packed into the same ASU as long as there's room.
    Unfortunately, since there's no way to discover how big an
    ASU is, you can't draw many conclusions from this.

    - If the struct contains two or more consecutive bit-fields
    and a later bit-field is too big for an ASU that's already
    partially filled, the compiler may start over with a fresh
    ASU (leaving "padding bits" in the old one) or may split the
    too-big bit-field across two or more ASU's. As before, since
    the compiler needn't disclose what packing strategy it uses
    nor how big an ASU is, this doesn't tell you much.

    - You can force the compiler to skip to a new ASU for the next
    bit-field, by using a nameless bit-field of width :0. But
    (broken record) since you don't know how big an ASU is ...

    - The arrangement of bits within an ASU is entirely up to the
    compiler. It can allocate successive bit-fields from high-
    order bits to low, or from low to high, or from the ends
    toward the middle, or any way it pleases. Same with padding
    bits: If there are any, they can be anywhere. And, as usual,
    the compiler needn't tell you how it chooses an arrangement.

    - A single bit-field contains no padding bits, and if it's signed
    it uses two's complement representation. But the arrangement
    of bits may be big-endian, little-endian, middle-endian, or
    end-over-endian -- and would you care to guess whether the
    compiler is obliged to document its choice?

    > I gather bitfields aren't very portable. Any wisdom on the above?


    Like the structs (or unions) that contain them, bit-fields are
    entirely portable[*] as a means to represent data within the program.
    And like the structs (or unions) that contain them, bit-fields are
    *not* suitable for matching externally-imposed formats. If somebody
    tells you about a packet containing a three-bit field, a five-bit
    field, and two four-bit fields, it's tempting to try to map it with
    bit-fields of :3, :5, :4, :4 bits. You may even get the mapping to
    work as desired with the C compiler on your favorite machine.

    ... but move the code to a different compiler, and things may
    well change. The individual bit-field elements will still be there,
    will still have their names, and will still be able to hold the
    same values, but there's very little you can be sure of about how
    they're laid out.

    [*] Well, "almost perfectly portable." If you just say int
    rather than signed int or unsigned int, the compiler gets to choose
    whether your bit-field is signed or not -- and different compilers
    may choose differently. Also, the only portable "base types" for a
    bit-field are the int flavors plus _Bool (C99); compilers may accept
    other types like short and uint8_t, but are not obliged to -- so if
    you use those other types with one compiler, the next might not accept
    them. Finally, a bit-field's width cannot exceed that of its base
    type -- so if you try a :24 bit-field on a machine with 16-bit int,
    you're out of luck. BUT, if you stick to signed int, unsigned int,
    and _Bool while avoiding other types (including plain int), and if
    you keep all the widths to :16 or less, bit-fields are "perfectly
    portable."[**]

    [**] This is supposed to be a comfort. Stop weeping.

    --
    Eric Sosman
    lid
     
    Eric Sosman, May 27, 2010
    #5
  6. On May 27, 11:36 am, Eric Sosman <> wrote:
    > On 5/27/2010 10:32 AM, David Resnick wrote:
    >
    > > I'm a bit confused about how bitfield organization works.

    >
    >      Don't feel bad: It's inherently confusing, because the compiler
    > has wide latitude in how it arranges things and the language imposes
    > very few restrictions.
    >


    Thanks very much for this (and the other) responses.

    Portable or not, I clearly can't represent as a single bitfield bits
    that are not contiguous. Since the underlying data layout treats 10
    bits that include the 2 LSB of one byte and the 8 bits of the next
    byte as a single entity, I just need to have two separate fields.

    And yes, this is a portability mess to treat the underlying data
    layout as a bitfield...

    -David
     
    David Resnick, May 27, 2010
    #6
  7. David Resnick

    Stargazer Guest

    On May 27, 6:36 pm, Eric Sosman <> wrote:
    [...]
    > If somebody
    > tells you about a packet containing a three-bit field, a five-bit
    > field, and two four-bit fields, it's tempting to try to map it with
    > bit-fields of :3, :5, :4, :4 bits.  You may even get the mapping to
    > work as desired with the C compiler on your favorite machine.
    >
    >      ... but move the code to a different compiler, and things may
    > well change.  The individual bit-field elements will still be there,
    > will still have their names, and will still be able to hold the
    > same values, but there's very little you can be sure of about how
    > they're laid out.


    Actually, there may be surprises even when moving with the same
    compiler to another architecture. E.g. the ways that GCC arranges bit-
    fields on x86 and big-endian ARM are quite different.

    Daniel
     
    Stargazer, May 30, 2010
    #7
  8. David Resnick

    Eric Sosman Guest

    On 5/30/2010 5:03 AM, Stargazer wrote:
    > On May 27, 6:36 pm, Eric Sosman<> wrote:
    > [...]
    >> If somebody
    >> tells you about a packet containing a three-bit field, a five-bit
    >> field, and two four-bit fields, it's tempting to try to map it with
    >> bit-fields of :3, :5, :4, :4 bits. You may even get the mapping to
    >> work as desired with the C compiler on your favorite machine.
    >>
    >> ... but move the code to a different compiler, and things may
    >> well change. The individual bit-field elements will still be there,
    >> will still have their names, and will still be able to hold the
    >> same values, but there's very little you can be sure of about how
    >> they're laid out.

    >
    > Actually, there may be surprises even when moving with the same
    > compiler to another architecture. E.g. the ways that GCC arranges bit-
    > fields on x86 and big-endian ARM are quite different.


    It's not "the same compiler" if it compiles for a different
    architecture. It's not even "the same compiler" if you run it for
    the same target architecture but with different option flags; for
    example, "gcc" and "gcc -ansi" and "gcc -std=c99" are different
    compilers.

    --
    Eric Sosman
    lid
     
    Eric Sosman, May 30, 2010
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. zb32

    bitfield optimizations

    zb32, Jul 13, 2004, in forum: C++
    Replies:
    1
    Views:
    1,068
    David Harmon
    Jul 13, 2004
  2. Claudio

    bitfield & union strange ?!

    Claudio, Aug 1, 2004, in forum: C++
    Replies:
    2
    Views:
    4,897
    Gottfried Eibner
    Aug 2, 2004
  3. Replies:
    0
    Views:
    524
  4. Davide Bruzzone
    Replies:
    9
    Views:
    382
    Adam S. Roan
    Aug 27, 2003
  5. Sushil
    Replies:
    1
    Views:
    700
    Jack Klein
    Nov 28, 2003
Loading...

Share This Page