Struct casting

Discussion in 'C Programming' started by Edward Rutherford, Dec 5, 2011.

  1. This was pulled up at a code review. Code was similar to this:

    struct foo {
    int bar;
    int baz;
    } f;

    int * i = (int *) f;

    The only way this could go wrong is if the struct contains padding before
    its first member, but I don't think this would ever happen in practice
    --- maybe on theoretical perverse implementations like the ones you guys
    like to play with :)

    Who is right here by the letter of the law?

    Cheers
    Edward
    Edward Rutherford, Dec 5, 2011
    #1
    1. Advertising

  2. On Dec 5, 10:52 pm, Edward Rutherford
    <> wrote:
    > This was pulled up at a code review. Code was similar to this:
    >
    > struct foo {
    >   int bar;
    >   int baz;
    >
    > } f;


    I'm assuming you meant } *f;

    >
    > int * i = (int *) f;


    because otherwise this should be a hard error.

    > The only way this could go wrong is if the struct contains padding before
    > its first member, but I don't think this would ever happen in practice


    A pointer to a struct can be safely converted to a pointer to its
    first member, and back. Padding before the first member is not
    allowed.

    That said, for various other reasons, relating to readability and
    maintainability, it may well be better to just write

    int *i = &f->bar;
    Harald van Dijk, Dec 5, 2011
    #2
    1. Advertising

  3. Edward Rutherford

    Tobias Blass Guest

    On 2011-12-05, Edward Rutherford <> wrote:
    > This was pulled up at a code review. Code was similar to this:
    >
    > struct foo {
    > int bar;
    > int baz;
    > } f;
    >
    > int * i = (int *) f;
    >
    > The only way this could go wrong is if the struct contains padding before
    > its first member, but I don't think this would ever happen in practice
    > --- maybe on theoretical perverse implementations like the ones you guys
    > like to play with :)
    >
    > Who is right here by the letter of the law?
    >

    The standard guarantees that there is no padding before the first element, so
    this conversion is absolutely valid. It may cause problems when another variable
    is placed before the 'bar',though, and I wouldn't bet that the person that adds
    the new member will change all these assignments.
    Tobias Blass, Dec 5, 2011
    #3
  4. Edward Rutherford

    tom st denis Guest

    On Dec 5, 4:52 pm, Edward Rutherford
    <> wrote:
    > This was pulled up at a code review. Code was similar to this:
    >
    > struct foo {
    >   int bar;
    >   int baz;
    >
    > } f;
    >
    > int * i = (int *) f;
    >
    > The only way this could go wrong is if the struct contains padding before
    > its first member, but I don't think this would ever happen in practice
    > --- maybe on theoretical perverse implementations like the ones you guys
    > like to play with :)
    >
    > Who is right here by the letter of the law?


    Is there a question of alignment though?

    Generally, that sort of code would fail review with me and my cohorts
    [er...coworkers]. If you wanted a specific element of a struct you
    should pick it by name.

    Tom
    tom st denis, Dec 5, 2011
    #4
  5. Edward Rutherford

    Ian Collins Guest

    On 12/ 6/11 11:00 AM, Tobias Blass wrote:
    > On 2011-12-05, Edward Rutherford<> wrote:
    >> This was pulled up at a code review. Code was similar to this:
    >>
    >> struct foo {
    >> int bar;
    >> int baz;
    >> } f;
    >>
    >> int * i = (int *) f;
    >>
    >> The only way this could go wrong is if the struct contains padding before
    >> its first member, but I don't think this would ever happen in practice
    >> --- maybe on theoretical perverse implementations like the ones you guys
    >> like to play with :)
    >>
    >> Who is right here by the letter of the law?
    >>

    > The standard guarantees that there is no padding before the first element, so
    > this conversion is absolutely valid.


    Not as written. There's a * in the declaration f or an & in the
    initialisation of i missing.

    > It may cause problems when another variable
    > is placed before the 'bar',though, and I wouldn't bet that the person that adds
    > the new member will change all these assignments.


    As mentioned else-thread,

    int *i = &f->bar;

    would be better.

    --
    Ian Collins
    Ian Collins, Dec 5, 2011
    #5
  6. Edward Rutherford

    Ian Collins Guest

    On 12/ 6/11 11:06 AM, tom st denis wrote:
    > On Dec 5, 4:52 pm, Edward Rutherford
    > <> wrote:
    >> This was pulled up at a code review. Code was similar to this:
    >>
    >> struct foo {
    >> int bar;
    >> int baz;
    >>
    >> } f;
    >>
    >> int * i = (int *) f;
    >>
    >> The only way this could go wrong is if the struct contains padding before
    >> its first member, but I don't think this would ever happen in practice
    >> --- maybe on theoretical perverse implementations like the ones you guys
    >> like to play with :)
    >>
    >> Who is right here by the letter of the law?

    >
    > Is there a question of alignment though?


    No.

    --
    Ian Collins
    Ian Collins, Dec 5, 2011
    #6
  7. Edward Rutherford

    Ian Collins Guest

    On 12/ 6/11 11:10 AM, Robert Wessel wrote:
    > On Mon, 5 Dec 2011 21:52:13 +0000 (UTC), Edward Rutherford
    > <> wrote:
    >
    >> This was pulled up at a code review. Code was similar to this:
    >>
    >> struct foo {
    >> int bar;
    >> int baz;
    >> } f;
    >>
    >> int * i = (int *) f;
    >>
    >> The only way this could go wrong is if the struct contains padding before
    >> its first member, but I don't think this would ever happen in practice
    >> --- maybe on theoretical perverse implementations like the ones you guys
    >> like to play with :)
    >>
    >> Who is right here by the letter of the law?

    >
    >
    > FWIW, C++ compilers frequently put the vtable pointer at the beginning
    > of classes/structs that contain virtual functions (IOW, the "padding"
    > at the beginning of the struct would be the vtable pointer)..
    >
    > In C, while two structures need to have the same layout in a common
    > leading area, I'm not sure there's a guarantee that the pointer to the
    > beginning of the beginning of the structure is required to be a
    > pointer to the first member.


    There is.

    --
    Ian Collins
    Ian Collins, Dec 5, 2011
    #7
  8. Edward Rutherford

    James Kuyper Guest

    On 12/05/2011 05:06 PM, tom st denis wrote:
    > On Dec 5, 4:52 pm, Edward Rutherford
    > <> wrote:
    >> This was pulled up at a code review. Code was similar to this:
    >>
    >> struct foo {
    >> int bar;
    >> int baz;
    >>
    >> } f;
    >>
    >> int * i = (int *) f;
    >>
    >> The only way this could go wrong is if the struct contains padding before
    >> its first member, but I don't think this would ever happen in practice
    >> --- maybe on theoretical perverse implementations like the ones you guys
    >> like to play with :)
    >>
    >> Who is right here by the letter of the law?

    >
    > Is there a question of alignment though?


    No, there is not. The standard guarantees that conversion of a pointer
    to a struct to a pointer to the type of the first member produces a
    valid pointer to that first member.

    > Generally, that sort of code would fail review with me and my cohorts
    > [er...coworkers]. If you wanted a specific element of a struct you
    > should pick it by name.


    Agreed. In addition to protecting against the possibility that bar might
    be moved to a different position, it's also more type-safe, since a type
    cast is no longer necessary.
    James Kuyper, Dec 5, 2011
    #8
  9. Edward Rutherford

    James Kuyper Guest

    On 12/05/2011 05:10 PM, Robert Wessel wrote:
    ....
    > In C, while two structures need to have the same layout in a common
    > leading area, I'm not sure there's a guarantee that the pointer to the
    > beginning of the beginning of the structure is required to be a
    > pointer to the first member.


    There is: 6.7.2.1p13.
    James Kuyper, Dec 5, 2011
    #9
  10. Edward Rutherford <> writes:
    > This was pulled up at a code review. Code was similar to this:
    >
    > struct foo {
    > int bar;
    > int baz;
    > } f;
    >
    > int * i = (int *) f;


    That should be

    int *i = (int*)&f;

    > The only way this could go wrong is if the struct contains padding before
    > its first member, but I don't think this would ever happen in practice
    > --- maybe on theoretical perverse implementations like the ones you guys
    > like to play with :)


    There cannot be any padding before the first member. The code is safe,
    but poor style. Presumably the member "bar" was given a name for a
    reason; just use that name:

    int *i = &f.bar;

    Unless, for some odd reason, you specifically want the address of
    the first member of the struct, and you're sure it's an int without
    being sure of its name.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Dec 5, 2011
    #10
  11. Edward Rutherford

    Ben Pfaff Guest

    Edward Rutherford <> writes:

    > Who is right here by the letter of the law?


    Others already gave the answer. Here's the quote from the
    standard:

    A pointer to a structure object, suitably converted, points
    to its initial member (or if that member is a bit-field,
    then to the unit in which it resides), and vice versa.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
    Ben Pfaff, Dec 5, 2011
    #11
  12. Edward Rutherford

    jacob navia Guest

    Le 05/12/11 22:52, Edward Rutherford a écrit :
    > This was pulled up at a code review. Code was similar to this:
    >
    > struct foo {
    > int bar;
    > int baz;
    > } f;
    >
    > int * i = (int *) f;
    >
    > The only way this could go wrong is if the struct contains padding before
    > its first member, but I don't think this would ever happen in practice
    > --- maybe on theoretical perverse implementations like the ones you guys
    > like to play with :)
    >
    > Who is right here by the letter of the law?
    >
    > Cheers
    > Edward


    This is a mistake. Instead of writing

    int *i = &f.bar; // 16 characters

    you write

    int * i = (int *) f; // 20 characters

    AND (most important):

    Now, the "bar" member MUST be the first member of the
    structure! And, as Murphy's Law states, sombody will
    add a member to the struct foo in the new version,
    2 years from here.

    WHO will remember that in another file there was an
    implicit dependency as to "bar" being the first member?

    This code is unmaintainable, it provokes a bug if
    ANY modification is done to "bar" within the structure.

    Suppose that "bar" member is eliminated. Instead of
    provokinbg a compiler error and forcing you to change
    the dependent code, the code will silently compile
    and provoke a VERY hard debugging session.

    I see no justification whatsoever for writing code
    like that.

    jacob

    P.S. Anyway your code doesn't compile, as others have said.
    jacob navia, Dec 6, 2011
    #12
  13. On Dec 6, 9:29 am, jacob navia <> wrote:
    >
    > int * i = (int *) f; // 20 characters
    >
    > I see no justification whatsoever for writing code
    > like that.
    >

    As the snippet goes, no.

    But "bar" might in fact be type a identifier field

    foodoo(struct foo *f)
    {
    switch( *( int *) f)
    {
    case REALLYABAR:
    dobar(f); return;
    case REALLYAFOO:
    dofoo(f); return;
    }
    }

    This is arguably better than switch(foo->bar) because it makes clear
    that code will onyl work if the identifier is the first member.
    Malcolm McLean, Dec 6, 2011
    #13
  14. Edward Rutherford

    Ian Collins Guest

    On 12/ 6/11 09:15 PM, Malcolm McLean wrote:
    > On Dec 6, 9:29 am, jacob navia<> wrote:
    >>
    >> int * i = (int *) f; // 20 characters
    >>
    >> I see no justification whatsoever for writing code
    >> like that.
    >>

    > As the snippet goes, no.
    >
    > But "bar" might in fact be type a identifier field
    >
    > foodoo(struct foo *f)
    > {
    > switch( *( int *) f)
    > {
    > case REALLYABAR:
    > dobar(f); return;
    > case REALLYAFOO:
    > dofoo(f); return;
    > }
    > }
    >
    > This is arguably better than switch(foo->bar) because it makes clear
    > that code will onyl work if the identifier is the first member.


    Better still is to give the first member a name that identifies is as
    the type identifier field. The X XEvent union is a good example of this.

    --
    Ian Collins
    Ian Collins, Dec 6, 2011
    #14
  15. Edward Rutherford

    Lauri Alanko Guest

    In article <>,
    Keith Thompson <> wrote:
    > There cannot be any padding before the first member. The code is safe,
    > but poor style. Presumably the member "bar" was given a name for a
    > reason; just use that name:
    >
    > int *i = &f.bar;
    >
    > Unless, for some odd reason, you specifically want the address of
    > the first member of the struct, and you're sure it's an int without
    > being sure of its name.


    It's a common idiom to simulate poor man's subtyping for structs by
    placing the supertype struct as the first member of the subtype
    struct, and then casting between struct pointer types. It may even be
    that the members of the structs are hidden from client code (or at
    least considered private), so the public interface might only say that
    a subtype struct pointer can be cast to the supertype pointer (and
    back again) without saying anything about the names of the members.

    Then again, the direct address-of-member approach, along with a
    containerof macro, gives you multiple inheritance, which might be
    useful.


    Lauri
    Lauri Alanko, Dec 6, 2011
    #15
  16. Edward Rutherford

    Joe keane Guest

    In article <jbjeed$c1v$>,
    Edward Rutherford <> wrote:
    >Who is right here by the letter of the law?


    Letter of law, 'good idea' is often different.

    It's right, but when someone comes along and mofidies your code,
    is it likely to do what they expect?

    Of course you put a comment that that member needs to be first, right?
    Joe keane, Dec 8, 2011
    #16
    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. kevin
    Replies:
    11
    Views:
    5,775
    Andrew McDonagh
    Jan 8, 2005
  2. Chandra Shekhar Kumar

    type casting of a ptr to a c struct

    Chandra Shekhar Kumar, Jun 24, 2003, in forum: C++
    Replies:
    3
    Views:
    2,019
    Rob Williscroft
    Jun 25, 2003
  3. Chris Fogelklou
    Replies:
    36
    Views:
    1,335
    Chris Fogelklou
    Apr 20, 2004
  4. Wally Barnes
    Replies:
    3
    Views:
    506
    Wally Barnes
    Nov 20, 2008
  5. Sosuke

    Up casting and down casting

    Sosuke, Dec 20, 2009, in forum: C++
    Replies:
    2
    Views:
    541
    James Kanze
    Dec 20, 2009
Loading...

Share This Page