Using offsetof to create a pointer to the start of a struct?

Discussion in 'C Programming' started by Not Really Me, Mar 13, 2008.

  1. We have run into some code that is using a hand calculated offset to
    dereference a pointer to a struct element to create a pointer to the start
    of the struct.

    (Example code below), basically a function is passed a pointer to a struct
    member that is not the first member in the struct. The function actually
    needs a pointer to the struct itself. To get that pointer, the code is
    subtracting the hand calculated offset of the member, from the from the
    pointer to the member, to create a pointer to the start of the struct. Ugly?

    The problem we see is that c99 6.3.2.3p5 says that subtracting an int from a
    pointer that is not pointing to an array is implementation dependent. In
    theory this should be portable code.

    To overcome this, someone suggested using the C99 offsetof macro to do get
    the offset. Otherwise the math remains the same.

    Is using offsetof still a violation of 6.3.2.3p5?

    Is so, is there any safe way to do this (short of rewriting the code to pass
    a pointer to the start of the struct)?

    struct { int apples, int oranges, int lemons, int grapes, int limes } Fruit;

    struct Fruit *pfruit;

    foo( pfruit->grapes);

    void foo( int *purple_fruit )
    {
    struct Fruit *local_fruit;
    int apple;

    local_fruit = &purple_fruit->grapes - offsetof( Fruit, grapes);
    apple = local_fruit->apples;
    }

    Scott
    Not Really Me, Mar 13, 2008
    #1
    1. Advertising

  2. On Mar 14, 10:03 am, "Not Really Me" wrote:
    > We have run into some code that is using a hand
    > calculated offset to dereference a pointer to a
    > struct element to create a pointer to the start
    > of the struct.
    >
    > (Example code below), basically a function is passed
    > a pointer to a struct member that is not the first
    > member in the struct.  The function actually needs a
    > pointer to the struct itself.  To get that pointer,
    > the code is subtracting the hand calculated offset
    > of the member, from the from the pointer to the member,
    > to create a pointer to the start of the struct. Ugly?


    Yes, offsetof is definitely better. But this sort of
    thing can be dangerous and errors can be hard to debug.

    > The problem we see is that c99 6.3.2.3p5 says that
    > subtracting an int from a pointer that is not pointing
    > to an array is implementation dependent.


    No, 6.3.2.3p5 talks about conversion of an integer to
    a pointer.

    > In theory this should be portable code.


    Not quite.

    > To overcome this, someone suggested using the C99
    > offsetof macro to do get the offset.


    It's available in C90 too.

    > Otherwise the math remains the same.
    >
    > Is using offsetof still a violation of 6.3.2.3p5?


    No. Subtraction of an integer from a pointer does
    not require the integer to be converted to a pointer.

    > Is so, is there any safe way to do this (short of
    > rewriting the code to pass a pointer to the start
    > of the struct)?
    >
    > struct { int apples, int oranges, int lemons,
    > int grapes, int limes } Fruit;
    >
    > struct Fruit *pfruit;
    >
    > foo( pfruit->grapes);
    >
    > void foo( int *purple_fruit )
    > {
    >     struct Fruit *local_fruit;
    >     int          apple;
    >
    >     local_fruit = &purple_fruit->grapes -
    > offsetof( Fruit, grapes);


    A few things...

    This takes a pointer to the parameter, which is not what
    you want.

    The offsetof macro takes a _type_ as the first argument,
    not an object.

    Pointer subtraction is in units of the element being
    pointed to, whereas offsetof returns a count in bytes.
    [So you need a byte/character pointer to use it
    effectively.]

    size_t grapes_delta = offsetof(struct Fruit, grapes);
    char *grapes_ptr = (char *) purple_fruit;
    local_fruit = (struct Fruit *) (grapres_ptr - grapes_delta);

    Or without temporaries...

    local_fruit =
    (struct Fruit *)
    (
    ((char *) purple_fruit)
    - offsetof(struct Fruit, grapes)
    );

    Strictly speaking, I think this violates the literal
    interpretation of 6.5.6 (Additive operators), but I
    can't see how it violates the intent, particularly
    of offsetof.

    >     apple = local_fruit->apples;
    >
    > }


    Passing a pointer to struct Fruit may well be the better
    option, not merely in terms of semantics, but also in
    terms of design.

    --
    Peter
    Peter Nilsson, Mar 14, 2008
    #2
    1. Advertising

  3. Not Really Me

    Jack Klein Guest

    On Thu, 13 Mar 2008 17:03:56 -0600, "Not Really Me"
    <> wrote in comp.lang.c:

    > We have run into some code that is using a hand calculated offset to
    > dereference a pointer to a struct element to create a pointer to the start
    > of the struct.
    >
    > (Example code below), basically a function is passed a pointer to a struct
    > member that is not the first member in the struct. The function actually
    > needs a pointer to the struct itself. To get that pointer, the code is
    > subtracting the hand calculated offset of the member, from the from the
    > pointer to the member, to create a pointer to the start of the struct. Ugly?
    >
    > The problem we see is that c99 6.3.2.3p5 says that subtracting an int from a
    > pointer that is not pointing to an array is implementation dependent. In
    > theory this should be portable code.


    You just lost me here. In my C99, original and including TC3,
    6.3.2.3, is Conversions, Other operands, Pointers, and paragraph 5
    begins with the sentence "An integer may be converted to any pointer
    type."

    Perhaps you have mistyped the reference?

    I'm looking at paragraph 8 of 6.5.6 Additive operators, which starts
    with the phrase "When an expression that has integer type is added to
    or subtracted from a pointer...".

    > To overcome this, someone suggested using the C99 offsetof macro to do get
    > the offset. Otherwise the math remains the same.
    >
    > Is using offsetof still a violation of 6.3.2.3p5?


    Again, I don't see that the paragraph you cite has any relevance at
    all.

    > Is so, is there any safe way to do this (short of rewriting the code to pass
    > a pointer to the start of the struct)?


    There is a safe and portable way to do this using offsetof, and this
    is exactly the purpose of the macro, to allow a safe and standardized
    way of doing things that you can't portably do without it.

    But first you have to pull together some other things from other parts
    of the standard.

    6.2.6.1 p4 "Values stored in non-bit-field objects of any other object
    type consist of n × CHAR_BIT bits, where n is the size of an object of
    that type, in bytes."

    6.5 p7 "An object shall have its stored value accessed only by an
    lvalue expression that has one of the following types:

    [snip all but last bullet item]

    — a character type."

    Essentially, any object can be treated as an array of bytes equal to
    the size of the object.

    > struct { int apples, int oranges, int lemons, int grapes, int limes } Fruit;
    >
    > struct Fruit *pfruit;
    >
    > foo( pfruit->grapes);
    >
    > void foo( int *purple_fruit )
    > {
    > struct Fruit *local_fruit;
    > int apple;
    >
    > local_fruit = &purple_fruit->grapes - offsetof( Fruit, grapes);
    > apple = local_fruit->apples;
    > }


    If you're going to post example code, instead of real code, you should
    at least make sure that it is compilable, which the above is not due
    to a large number of errors.

    But the complete program below:

    #include <stdio.h>
    #include <stddef.h>

    struct Fruit { int apples; int oranges; int lemons; int grapes; int
    limes; };

    void foo( int *purple_fruit )
    {
    struct Fruit *local_fruit;
    local_fruit = (struct Fruit *)((char *)purple_fruit -
    offsetof(struct Fruit, grapes));

    printf("local_fruit = %p\n", local_fruit);
    }

    int main(void)
    {
    struct Fruit my_fruit;
    struct Fruit *pfruit = &my_fruit;

    printf("pfruit = %p\n", pfruit);
    foo( &pfruit->grapes);
    return 0;
    }

    ....is perfectly conforming and portable.

    By casting a pointer to any member of an aggregate object to a
    character type, you now effectively have a pointer into an array of
    character types the size of the aggregate object. Any pointer
    additions or subtractions you do are perfectly valid as long as the
    result lies within that array of bytes, that is the boundaries of the
    original aggregate object, or to one past the last byte.

    Since the offsetof macro returns an offset, in bytes, you can directly
    subtract the value yielded from a pointer to char converted from the
    address of the corresponding member, and yield the address of the
    first byte of the structure type. And then you can convert, with a
    cast, to a pointer to a structure type and it is guaranteed to point
    to the full structure.

    This is 100% portable to any conforming C90 or later implementation.

    > Scott


    --
    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.learn.c-c++
    http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Mar 14, 2008
    #3
    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. Mark A. Odell

    Obtain sizeof struct element using offsetof()?

    Mark A. Odell, Sep 27, 2004, in forum: C Programming
    Replies:
    10
    Views:
    2,157
    Peter Shaggy Haywood
    Oct 1, 2004
  2. beetle
    Replies:
    2
    Views:
    902
    beetle
    Jan 25, 2005
  3. Michael B Allen

    offsetof(struct foo, bar.mem)?

    Michael B Allen, Sep 2, 2005, in forum: C Programming
    Replies:
    3
    Views:
    602
    Peter Nilsson
    Sep 5, 2005
  4. Zero
    Replies:
    16
    Views:
    651
    Barry Schwarz
    Nov 19, 2005
  5. Urs Thuermann
    Replies:
    6
    Views:
    2,018
    Harald van =?UTF-8?B?RMSzaw==?=
    May 25, 2007
Loading...

Share This Page