Accessing bit fields of a structure

Discussion in 'C Programming' started by lancer6238@yahoo.com, Jun 15, 2009.

  1. Guest

    Hi,

    I'm trying to write a structure to represent the header of a GRE
    packet, then access each component in the header. However, I have
    problems getting the right values for the bit fields.

    Here's what I have:

    struct grehdrv0
    {
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int version:3;
    unsigned int flags:5;
    unsigned int recur:3;
    unsigned int s:1;
    unsigned int S:1;
    unsigned int K:1;
    unsigned int R:1;
    unsigned int C:1;
    #elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int C:1;
    unsigned int R:1;
    unsigned int K:1;
    unsigned int S:1;
    unsigned int s:1;
    unsigned int recur:3;
    unsigned int flags:5;
    unsigned int version:3;
    #else
    #error "Please fix <bits/endian.h>
    u_int16_t protocol;
    /* Optional fields */
    }

    I'm trying to access the bit fields using:

    struct grehdrv0 *gre;
    ....
    // gre now points to the gre header portion of the packet
    printf("%d %d %d %d %d %d %d %d %x\n", gre->C, gre->R, gre->K, gre->S,
    gre->s, gre->recur, gre->flags, gre->version, ntohs(gre->protocol));

    The first 16 bits should be (in binary) 0011 0000 1000 0001. However,
    I am unable to get the correct values for the first 16 bits of the
    header (I got in decimal 1 0 0 0 0 1 6 0), but gre->protocol gives me
    the correct value.

    How do I access the bit fields?

    Thank you.
     
    , Jun 15, 2009
    #1
    1. Advertising

  2. <> wrote:
    >I'm trying to write a structure to represent the header of a GRE
    >packet, then access each component in the header. However, I have
    >problems getting the right values for the bit fields.


    It's so tempting to just overlay a bitfield struct right onto the packet
    data, isn't it? It's perfect! I mean, what could possibly go wrong? :)

    The problem is the compiler is free to take extreme liberties with
    bitfields, so they're not guaranteed (or perhaps even likely) to be
    packed in the way you hope.

    C99 6.7.2.1p10:

    # An implementation may allocate any addressable storage unit large
    # enough to hold a bitfield. If enough space remains, a bit-field that
    # immediately follows another bit-field in a structure shall be packed
    # into adjacent bits of the same unit. If insufficient space remains,
    # whether a bit-field that does not fit is put into the next unit or
    # overlaps adjacent units is implementation-defined. The order of
    # allocation of bit-fields within a unit (high-order to low-order or
    # low-order to high-order) is implementation-defined. The alignment of
    # the addressable storage unit is unspecified.

    So all bets are off. It looks like you're accessing the bitfields
    correctly when you print them, but the bitfields in the struct aren't
    aligning properly with the raw packet data (because the compiler is
    allowed to change their alignment, allocation order, etc.)

    You're going to have to do it the old-fashioned way and manually check
    which bits are set by peering into your char buffer that holds the
    packet. Macros might make your life easier in this case.

    -Beej
     
    Beej Jorgensen, Jun 15, 2009
    #2
    1. Advertising

  3. On Jun 15, 3:18 pm, "" <>
    wrote:
    > Hi,
    > #if __BYTE_ORDER == __LITTLE_ENDIAN
    > #elif __BYTE_ORDER == __BIG_ENDIAN


    For your purpose there may be more than two types
    of "Endianness". Three or four patterns *might* cover
    all cases.

    > unsigned int ... :3,5,3,1,1,1,1,1
    >
    > The first 16 bits should be (in binary) 0011 0000 1000 0001


    Let's change the spacing in that binary number
    and assign fields empirically.

    00110 000 1 0 0 0 0 001.
    [5]=6 [3]=0 [1]=1 [1]=0 [1]=0 [1]=0 [1]=0 [3]=1

    > However, ... (I got in decimal 1 0 0 0 0 1 6 0),


    I'm surprised you didn't experiment with more data values.
    I had little trouble figuring out the straightforward
    bit assignment, but with, e.g. a single 1-bit rippling
    it would have become very obvious.

    On Jun 15, 4:09 pm, Beej Jorgensen <> wrote:
    >
    > The problem is the compiler is free to take extreme liberties with
    > bitfields, so they're not guaranteed (or perhaps even likely) to be
    > packed in the way you hope.
    >
    > [Standard excerpt snipped]


    Note that OP's example does *not* have fields spanning an 8-bit
    boundary,
    and is a nice multiple of 8 bits in length. The standard excerpt
    quoted *might* even lead to a *proof* that at most 4 patterns are
    possible, but I'll leave that to the C lawyers.

    > So all bets are off....
    > You're going to have to do it the old-fashioned way...


    I'm certainly no C lawyer, but I'd bet 3 or 4 cases would
    cover everything except, possibly, "hypothetical" compilers.
    But, rather than coding a painful 'config', it would
    be easiest to follow Beej's suggestion and avoid bitfields.

    The reason I'm following up is that *I continue to believe*
    that some "C lawyers" insist on coding for compilers that
    are about as real as the Tooth Fairy. In a recent thread,
    I changed code to allow for padding bits. Eventually,
    the resident expert admitted machines with such padding
    "might be purely hypothetical."

    James Dow Allen
     
    James Dow Allen, Jun 15, 2009
    #3
  4. Boon Guest

    lancer wrote:

    > I'm trying to write a structure to represent the header of a GRE
    > packet, then access each component in the header. However, I have
    > problems getting the right values for the bit fields.
    >
    > Here's what I have:
    >
    > struct grehdrv0
    > {
    > #if __BYTE_ORDER == __LITTLE_ENDIAN
    > unsigned int version:3;
    > unsigned int flags:5;
    > unsigned int recur:3;
    > unsigned int s:1;
    > unsigned int S:1;
    > unsigned int K:1;
    > unsigned int R:1;
    > unsigned int C:1;
    > #elif __BYTE_ORDER == __BIG_ENDIAN
    > unsigned int C:1;
    > unsigned int R:1;
    > unsigned int K:1;
    > unsigned int S:1;
    > unsigned int s:1;
    > unsigned int recur:3;
    > unsigned int flags:5;
    > unsigned int version:3;
    > #else
    > #error "Please fix <bits/endian.h>
    > u_int16_t protocol;
    > /* Optional fields */
    > }
    >
    > I'm trying to access the bit fields using:
    >
    > struct grehdrv0 *gre;
    > ...
    > // gre now points to the gre header portion of the packet
    > printf("%d %d %d %d %d %d %d %d %x\n", gre->C, gre->R, gre->K, gre->S,
    > gre->s, gre->recur, gre->flags, gre->version, ntohs(gre->protocol));
    >
    > The first 16 bits should be (in binary) 0011 0000 1000 0001. However,
    > I am unable to get the correct values for the first 16 bits of the
    > header (I got in decimal 1 0 0 0 0 1 6 0), but gre->protocol gives me
    > the correct value.


    1 0 0 0 0 1 6 0 <-> 10000001 00110000

    What does your program print given

    struct grehdrv0 foo;
    struct grehdrv0 *gre = &foo;
    uint8_t *p = (uint8_t *)gre;
    p[0] = 0x47;
    p[1] = ~0x47;

    > How do I access the bit fields?


    Do you need your code to be portable?

    CHAR_BIT > 8 adds a layer of complexity.

    Can you assume POSIX? (which mandates CHAR_BIT = 8)

    Regards.
     
    Boon, Jun 15, 2009
    #4
  5. James Kuyper Guest

    James Dow Allen wrote:
    ....
    > The reason I'm following up is that *I continue to believe*
    > that some "C lawyers" insist on coding for compilers that
    > are about as real as the Tooth Fairy.


    When I write C code, I write it for compilers which conform fully to the
    C standard. I refuse to write C code which make assumptions about
    matters that the C standard leaves unspecified unless they are instead
    specified as part of the requirements for the program I'm writing. Some
    of the assumptions I refuse to make might be violated only by compilers
    no more real than the Tooth Fairy; but that doesn't mean I'm actually
    coding for such compilers. I suspect that such assumptions are rarer
    than you think they are; in many cases, compilers violating the
    assumption are only as unreal as gcc 6.0.0 - i.e. they are compilers
    that haven't been written yet, but might get written before the lifetime
    of my code ends.

    I just find it easier to follow a consistent rule, than to evaluate each
    possible deviation from that rule for it's likelihood of causing problems.
     
    James Kuyper, Jun 15, 2009
    #5
  6. James Dow Allen <> wrote:
    >Note that OP's example does *not* have fields spanning an 8-bit
    >boundary,


    This is a keen observation that I missed; nevertheless, with the
    possibility of padding insertion and bit reversal, it's still going to
    be "interesting" to get the overlain struct to make sense in all cases.

    >The standard excerpt quoted *might* even lead to a *proof* that at most
    >4 patterns are possible, but I'll leave that to the C lawyers.


    This would be an interesting thing to see. With the the amount of
    padding between struct elements being variable, I would think it would
    be difficult to code for.

    >The reason I'm following up is that *I continue to believe* that some
    >"C lawyers" insist on coding for compilers that are about as real as
    >the Tooth Fairy.


    Well, at least in this case, that wasn't my intent. :) I distinctly
    remember trying to do exactly this thing years ago with packets,
    structs, and bitfields, and having it fail.

    And I don't have anything against non-portable code when you know what
    you're getting into. (I've calculated the length of a function by
    subtracting function pointers--and I'm not even going to apologize!) But
    in this case, the struct layout in memory could change on the same
    platform with the same compiler just by changing the optimization
    flags... it seems like it's just asking for trouble--proper trouble--to
    code around it.

    -Beej
     
    Beej Jorgensen, Jun 15, 2009
    #6
  7. Beej Jorgensen <> writes:

    > James Dow Allen <> wrote:
    >>Note that OP's example does *not* have fields spanning an 8-bit
    >>boundary,

    >
    > This is a keen observation that I missed; nevertheless, with the
    > possibility of padding insertion and bit reversal, it's still going to
    > be "interesting" to get the overlain struct to make sense in all
    > cases.


    Padding is not permitted, at least not "in general". Consecutive
    fields must be packed into the same storage unit and the
    implementation must document the order in which this happens. This is
    why James' observation is a good one. If you are writing
    non-portable code, you *can* rely on some properties of bit fields.

    I agree that they are a potability nightmare, but that is not always
    the issue.

    >>The standard excerpt quoted *might* even lead to a *proof* that at most
    >>4 patterns are possible, but I'll leave that to the C lawyers.

    >
    > This would be an interesting thing to see. With the the amount of
    > padding between struct elements being variable, I would think it would
    > be difficult to code for.


    Padding of bit fields can only happen when the size is such that it
    does not fit in the space remaining in the current "unit". The
    implementation must also document what happens in this case and there
    are only two possibilities.

    <snip>
    > And I don't have anything against non-portable code when you know what
    > you're getting into. (I've calculated the length of a function by
    > subtracting function pointers--and I'm not even going to apologize!) But
    > in this case, the struct layout in memory could change on the same
    > platform with the same compiler just by changing the optimization
    > flags... it seems like it's just asking for trouble--proper trouble--to
    > code around it.


    Not likely. There would have to be some odd flag like
    -use-optimal-9-bit-chars for this to occur and, if it does, the
    implementation must document the new packing rules or it will not be
    conforming.

    Bit fields have huge problems, but they have fewer problems than they
    are often portrayed as having!

    --
    Ben.
     
    Ben Bacarisse, Jun 15, 2009
    #7
  8. Beej Jorgensen <> writes:
    [...]
    > And I don't have anything against non-portable code when you know what
    > you're getting into. (I've calculated the length of a function by
    > subtracting function pointers--and I'm not even going to apologize!) But
    > in this case, the struct layout in memory could change on the same
    > platform with the same compiler just by changing the optimization
    > flags... it seems like it's just asking for trouble--proper trouble--to
    > code around it.


    Varying struct layout depending on compiler flags is permitted (each
    set of flags effectively specifies a distinct implementation as
    far as the standard is concerned), but it could cause some serious
    problems. For example, if a struct is declared in a header file,
    then all translation units that include that header, directly or
    indirectly, would have to be compiled with the same flags. If the
    header is provided as part of a library, then all client code would
    have to be compiled with the same flags.

    I've seen compiler flags that control whether plain char is signed
    or unsigned, and whether pointers are 32 or 64 bits, and such flags
    do have to be consistent for all translation units within a program.
    But I wouldn't expect an *optimization* flag to affect struct layout.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Jun 15, 2009
    #8
  9. Beej Guest

    On Jun 15, 10:59 am, Keith Thompson <> wrote:
    > But I wouldn't expect an *optimization* flag to affect struct layout.


    I was thinking more "optimize for size", in this case. Like with
    gcc's -fpack-struct option. But yeah, I agree it could lead to all
    kinds of interesting stuff.

    -Beej
     
    Beej, Jun 16, 2009
    #9
  10. Beej Guest

    On Jun 15, 10:26 am, Ben Bacarisse <> wrote:
    > Bit fields have huge problems, but they have fewer problems than they
    > are often portrayed as having!


    comp.lang.c.bitfields.die.die.die! ;) No, I don't really have a
    problem with them in general (you can save tons of space if you're
    allocating a pile of structs), but in this particular case with the
    packet data, I still think it's more trouble than it's worth trying to
    get the struct to line up properly and portably.

    -Beej
     
    Beej, Jun 16, 2009
    #10
    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. Eric Sosman

    bit fields in a structure

    Eric Sosman, Apr 20, 2004, in forum: C Programming
    Replies:
    2
    Views:
    4,441
  2. Hetal

    bit fields in a structure

    Hetal, May 17, 2004, in forum: C Programming
    Replies:
    2
    Views:
    546
    Peter Shaggy Haywood
    May 22, 2004
  3. Replies:
    3
    Views:
    1,855
    Timothy Bendfelt
    Jan 19, 2007
  4. ma740988

    bit fields and data structure

    ma740988, Nov 17, 2008, in forum: C++
    Replies:
    7
    Views:
    598
    ma740988
    Dec 28, 2008
  5. Mike -- Email Ignored

    Bit Order in Bit Fields

    Mike -- Email Ignored, May 2, 2009, in forum: C++
    Replies:
    0
    Views:
    417
    Mike -- Email Ignored
    May 2, 2009
Loading...

Share This Page