Bitfield casting

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • arne

    #1

    Bitfield casting

    Hi all,

    cleaning up some elderly code, I stumbled across the following:

    /*************** *************** *************** *****/

    struct {
    uint bf:8;
    char a1[8];
    char a2[8];
    } binfo;

    /* prototype added later */
    int foo( char * );

    int main( void )
    {
    foo( &binfo );

    return 0;
    }

    int foo( char *c ) {

    /*
    ** do something with c here ...
    */

    return 0;
    }

    /*************** *************** *************** *****/

    Note: The address of the bitfield bf of the struct binfo is used as
    the
    char* arg in foo().

    The compiler was complaining, since there was no prototype for foo().
    After adding it, the compiler was complaining, since the call of foo()
    did
    not match the prototype (which I can understand, since &binfo is
    certainly
    not exactly of type char*).

    None the less, I have some questions:

    1. Does it change the semantic of the code in any circumstances
    (little
    vs big endian, 64 bit architectures, ...) if I cast the argument of
    foo like

    foo( (char *)&binfo );

    ?
    If the explicit cast is omitted, isn't the same cast done implicitely
    anyway? If
    yes: also if there is no prototype or what happens then?

    2. Is &binfo _always_ pointing to the 8 bit wide bitfield bf (more
    general:
    are the addresses of a struct and of its first member always
    identical)?

    3. Are the addresses of members in a struct always ascending in the
    same
    order as the members have been defined? Or is the compiler allowed to
    do some reordering (not padding)?


    Thanks in advance for your responses!
    Arne

  • Eric Sosman

    #2
    Re: Bitfield casting

    arne wrote On 03/06/07 15:07,:
    Hi all,
    >
    cleaning up some elderly code, I stumbled across the following:
    >
    /*************** *************** *************** *****/
    >
    struct {
    uint bf:8;
    I'll assume `uint' is a typedef alias for `unsigned int'.
    char a1[8];
    char a2[8];
    } binfo;
    >
    /* prototype added later */
    int foo( char * );
    >
    int main( void )
    {
    foo( &binfo );
    >
    return 0;
    }
    >
    int foo( char *c ) {
    >
    /*
    ** do something with c here ...
    */
    >
    return 0;
    }
    >
    /*************** *************** *************** *****/
    >
    Note: The address of the bitfield bf of the struct binfo is used as
    the
    char* arg in foo().
    >
    The compiler was complaining, since there was no prototype for foo().
    After adding it, the compiler was complaining, since the call of foo()
    did
    not match the prototype (which I can understand, since &binfo is
    certainly
    not exactly of type char*).
    >
    None the less, I have some questions:
    >
    1. Does it change the semantic of the code in any circumstances
    (little
    vs big endian, 64 bit architectures, ...) if I cast the argument of
    foo like
    >
    foo( (char *)&binfo );
    The cast will (probably) cause the compiler to stop
    complaining, but won't fix the latent bug.
    ?
    If the explicit cast is omitted, isn't the same cast done implicitely
    anyway?
    No; that's why the compiler complains.
    If yes: also if there is no prototype or what happens then?
    If there is no prototype, then the argument &binfo is
    simply *assumed* to be of the type foo() expects to receive.
    Since it isn't of the right type, you get undefined behavior.
    2. Is &binfo _always_ pointing to the 8 bit wide bitfield bf
    &binfo is *never* pointing to the bit-field bf, because
    bitfields can't be pointed to at all.
    (more
    general:
    are the addresses of a struct and of its first member always
    identical)?
    Yes, a pointer to a struct can always be converted to a
    pointer to its first element, and vice versa. But the first
    element of binfo is not the bit-field bf; instead, it is the
    "addressabl e storage unit" that holds the bit-field. That
    "unit" has at least eight bits, but may have more -- and if
    it does, you don't know which bits are part of bf and which
    are merely along for the ride.
    3. Are the addresses of members in a struct always ascending in the
    same
    order as the members have been defined? Or is the compiler allowed to
    do some reordering (not padding)?
    Padding is permitted anywhere except before the first
    element. Reordering is not permitted, so a2 comes after a1
    which in turn comes after the "unit" that holds bf.

    --
    Eric.Sosman@sun .com

    Comment

    • Keith Thompson

      #3
      Re: Bitfield casting

      "arne" <arne.wiebalck@ gmail.comwrites :
      cleaning up some elderly code, I stumbled across the following:
      >
      /*************** *************** *************** *****/
      >
      struct {
      uint bf:8;
      char a1[8];
      char a2[8];
      } binfo;
      >
      /* prototype added later */
      int foo( char * );
      >
      int main( void )
      {
      foo( &binfo );
      >
      return 0;
      }
      >
      int foo( char *c ) {
      >
      /*
      ** do something with c here ...
      */
      >
      return 0;
      }
      >
      /*************** *************** *************** *****/
      >
      Note: The address of the bitfield bf of the struct binfo is used as
      the char* arg in foo().
      [snip]

      No, it isn't; bit fields don't have addresses.

      There is no predefined type called "uint". I presume it's a typedef
      for "unsigned int"; it would be better just to use "unsigned int"
      explicitly.

      Once you "clean up" the code, probably by judiciously adding casts,
      it's likely to do what you want, but there's no guarantee. The
      standard guarantees that a bit field will occupy the specified number
      of bits, but not much else (there are some additional guarantees, but
      they leave enough wiggle room for the compiler to do some odd things).

      If you want the first member of your structure to be 8 bits and
      unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
      just declare it as an unsigned char, not as a bit field.

      Any object can be treated as an array of bytes (unsigned char is
      usually better than plain char for this), but code that does so is
      likely to depend on the compiler-specific representation of the type.

      --
      Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
      San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
      "We must do something. This is something. Therefore, we must do this."
      -- Antony Jay and Jonathan Lynn, "Yes Minister"

      Comment

      • Jack Klein

        #4
        Re: Bitfield casting

        On Tue, 06 Mar 2007 13:00:10 -0800, Keith Thompson <kst-u@mib.org>
        wrote in comp.lang.c:
        "arne" <arne.wiebalck@ gmail.comwrites :
        cleaning up some elderly code, I stumbled across the following:

        /*************** *************** *************** *****/

        struct {
        uint bf:8;
        char a1[8];
        char a2[8];
        } binfo;
        [snip]
        If you want the first member of your structure to be 8 bits and
        unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
        just declare it as an unsigned char, not as a bit field.
        Why the need to assume CHAR_BIT 8? If you want the first member of
        your structure to hold 8 bits and be unsigned, regardless of whether
        you use an unsigned char and bit fiddle, or use a bit-field containing
        8 bits, it will occupy at least the first byte of your structure,
        regardless of whether CHAR_BIT is 8, 16, 32, or some other value.

        In fact the one guarantee that you have is that if you define it as an
        unsigned char, it will occupy exactly one byte at the beginning of
        your structure, whereas a bit-field containing 8 bits might occupy
        more than one byte, whatever the implementation' s value of CHAR_BIT.

        Of course, depending on what follows, padding after the first member
        might moot the difference.

        --
        Jack Klein
        Home: http://JK-Technology.Com
        FAQs for
        comp.lang.c http://c-faq.com/
        comp.lang.c++ http://www.parashift.com/c++-faq-lite/
        alt.comp.lang.l earn.c-c++

        Comment

        • Keith Thompson

          #5
          Re: Bitfield casting

          Jack Klein <jackklein@spam cop.netwrites:
          On Tue, 06 Mar 2007 13:00:10 -0800, Keith Thompson <kst-u@mib.org>
          wrote in comp.lang.c:
          >"arne" <arne.wiebalck@ gmail.comwrites :
          cleaning up some elderly code, I stumbled across the following:
          >
          /*************** *************** *************** *****/
          >
          struct {
          uint bf:8;
          char a1[8];
          char a2[8];
          } binfo;
          >
          [snip]
          >
          >If you want the first member of your structure to be 8 bits and
          >unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
          >just declare it as an unsigned char, not as a bit field.
          >
          Why the need to assume CHAR_BIT 8?
          I was assuming the OP wanted *exactly* 8 bits; I should have said so.
          If you want the first member of
          your structure to hold 8 bits and be unsigned, regardless of whether
          you use an unsigned char and bit fiddle, or use a bit-field containing
          8 bits, it will occupy at least the first byte of your structure,
          regardless of whether CHAR_BIT is 8, 16, 32, or some other value.
          >
          In fact the one guarantee that you have is that if you define it as an
          unsigned char, it will occupy exactly one byte at the beginning of
          your structure, whereas a bit-field containing 8 bits might occupy
          more than one byte, whatever the implementation' s value of CHAR_BIT.
          An 8-bit bit field will occupy exactly 8 bits; it can be less than a
          byte, but it can't be more.
          Of course, depending on what follows, padding after the first member
          might moot the difference.
          --
          Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
          San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
          "We must do something. This is something. Therefore, we must do this."
          -- Antony Jay and Jonathan Lynn, "Yes Minister"

          Comment

          • Jack Klein

            #6
            Re: Bitfield casting

            On Tue, 06 Mar 2007 16:06:59 -0800, Keith Thompson <kst-u@mib.org>
            wrote in comp.lang.c:
            Jack Klein <jackklein@spam cop.netwrites:
            On Tue, 06 Mar 2007 13:00:10 -0800, Keith Thompson <kst-u@mib.org>
            wrote in comp.lang.c:
            "arne" <arne.wiebalck@ gmail.comwrites :
            cleaning up some elderly code, I stumbled across the following:

            /*************** *************** *************** *****/

            struct {
            uint bf:8;
            char a1[8];
            char a2[8];
            } binfo;
            [snip]
            If you want the first member of your structure to be 8 bits and
            unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
            just declare it as an unsigned char, not as a bit field.
            Why the need to assume CHAR_BIT 8?
            >
            I was assuming the OP wanted *exactly* 8 bits; I should have said so.
            >
            If you want the first member of
            your structure to hold 8 bits and be unsigned, regardless of whether
            you use an unsigned char and bit fiddle, or use a bit-field containing
            8 bits, it will occupy at least the first byte of your structure,
            regardless of whether CHAR_BIT is 8, 16, 32, or some other value.

            In fact the one guarantee that you have is that if you define it as an
            unsigned char, it will occupy exactly one byte at the beginning of
            your structure, whereas a bit-field containing 8 bits might occupy
            more than one byte, whatever the implementation' s value of CHAR_BIT.
            >
            An 8-bit bit field will occupy exactly 8 bits; it can be less than a
            byte, but it can't be more.
            No, a bit-field occupies the amount of space taken up by the
            addressable storage unit that the implementation decides to use to
            store it.

            And there is nothing in the standard prohibiting a bit-field that does
            not use all of the bits in its addressable storage unit from having
            its bits non-contiguous.

            An application that places an 8-bit bit-field in a 16-bit storage unit
            is free to put one of the bits in alternate bits.
            Of course, depending on what follows, padding after the first member
            might moot the difference.
            --
            Jack Klein
            Home: http://JK-Technology.Com
            FAQs for
            comp.lang.c http://c-faq.com/
            comp.lang.c++ http://www.parashift.com/c++-faq-lite/
            alt.comp.lang.l earn.c-c++

            Comment

            • Keith Thompson

              #7
              Re: Bitfield casting

              Jack Klein <jackklein@spam cop.netwrites:
              On Tue, 06 Mar 2007 16:06:59 -0800, Keith Thompson <kst-u@mib.org>
              wrote in comp.lang.c:
              [...]
              >An 8-bit bit field will occupy exactly 8 bits; it can be less than a
              >byte, but it can't be more.
              >
              No, a bit-field occupies the amount of space taken up by the
              addressable storage unit that the implementation decides to use to
              store it.
              Hmm. You may be right, but I think it's more a matter of how it's
              described than of what's actually permitted.

              My assumption was that an N-bit bit-field occupies exactly N bits.
              There may be additional bits within the containing storage unit that
              don't contribute to the bit-field's value. I was thinking of those
              bits as being a kind of padding *between* members, not as padding bits
              within the bit-field. I don't think the standard is clear on which it
              is -- but I don't think it needs to be.

              C99 6.7.2.1p10:

              An implementation may allocate any addressable storage unit
              large enough to hold a bit-field. 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.

              I think you're interpreting the first sentence to mean that the the
              addressable storage unit is allocated *to the bit-field*. That's not
              an unreasonable interpretation, but the second sentence, which talks
              about possibly allocating the remaining space in that storage unit to
              another bit-field, tends to weaken that assumption.

              In any case, since you can't apply sizeof to a bit-field, the question
              of whether any "padding" bits are part of the bit-field or not isn't
              necessarily meaningful.
              And there is nothing in the standard prohibiting a bit-field that does
              not use all of the bits in its addressable storage unit from having
              its bits non-contiguous.
              >
              An application that places an 8-bit bit-field in a 16-bit storage unit
              is free to put one of the bits in alternate bits.
              Perhaps, but I think that would be a perverse implementation.

              --
              Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
              San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
              "We must do something. This is something. Therefore, we must do this."
              -- Antony Jay and Jonathan Lynn, "Yes Minister"

              Comment

              • Jack Klein

                #8
                Re: Bitfield casting

                On Tue, 06 Mar 2007 19:27:48 -0800, Keith Thompson <kst-u@mib.org>
                wrote in comp.lang.c:
                Jack Klein <jackklein@spam cop.netwrites:
                On Tue, 06 Mar 2007 16:06:59 -0800, Keith Thompson <kst-u@mib.org>
                wrote in comp.lang.c:
                [...]
                An 8-bit bit field will occupy exactly 8 bits; it can be less than a
                byte, but it can't be more.
                No, a bit-field occupies the amount of space taken up by the
                addressable storage unit that the implementation decides to use to
                store it.
                >
                Hmm. You may be right, but I think it's more a matter of how it's
                described than of what's actually permitted.
                >
                My assumption was that an N-bit bit-field occupies exactly N bits.
                There may be additional bits within the containing storage unit that
                don't contribute to the bit-field's value. I was thinking of those
                bits as being a kind of padding *between* members, not as padding bits
                within the bit-field. I don't think the standard is clear on which it
                is -- but I don't think it needs to be.
                >
                C99 6.7.2.1p10:
                >
                An implementation may allocate any addressable storage unit
                large enough to hold a bit-field. 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.
                >
                I think you're interpreting the first sentence to mean that the the
                addressable storage unit is allocated *to the bit-field*. That's not
                an unreasonable interpretation, but the second sentence, which talks
                about possibly allocating the remaining space in that storage unit to
                another bit-field, tends to weaken that assumption.
                >
                In any case, since you can't apply sizeof to a bit-field, the question
                of whether any "padding" bits are part of the bit-field or not isn't
                necessarily meaningful.
                >
                And there is nothing in the standard prohibiting a bit-field that does
                not use all of the bits in its addressable storage unit from having
                its bits non-contiguous.

                An application that places an 8-bit bit-field in a 16-bit storage unit
                is free to put one of the bits in alternate bits.
                >
                Perhaps, but I think that would be a perverse implementation.
                The good old DS9000 comes to mind...

                --
                Jack Klein
                Home: http://JK-Technology.Com
                FAQs for
                comp.lang.c http://c-faq.com/
                comp.lang.c++ http://www.parashift.com/c++-faq-lite/
                alt.comp.lang.l earn.c-c++

                Comment

                Working...