how best to get the offset of a field within a struct?

Discussion in 'C Programming' started by funkyj, Feb 17, 2006.

  1. funkyj

    funkyj Guest

    PROBLEM STATEMENT:
    I want to calculate the byte offset of a field with a struct at
    compile time without instantiating an instance of the struct at
    runtime AND I want to do this in an ANSI standard compliant
    fashion.

    DISCUSSION:

    Below is a sample program that demonstrates my solution. My questions
    are:

    (1) is this ANSI C compliant?
    (2) is there a better way to solve this problem?


    I think I'm on safe ground because casting and taking the address of
    stuff does not cause memory to be accessed (i.e. pointers
    dereferenced). In any event I seem to have satisfied GCC:

    > cd c:/cygwin/home/fj/offset/
    > make offset && ./offset
    > gcc -ansi -pedantic -o offset offset.c
    > MY_OFFSET(verybig_t, a) = 0x0
    > MY_OFFSET(verybig_t, g) = 0x20
    > MY_OFFSET(verybig_t, j) = 0x202a
    > MY_OFFSET(verybig_t, p) = 0x203a
    >
    > Compilation finished at Fri Feb 17 11:41:58


    Regards,
    --jfc

    ================ begin sample program offset.c ================
    #include <stdio.h>
    typedef struct {
    long a, b, c;
    short e, f;
    char name[13];
    long g, h;
    char stuff[1 << 13];
    short i;
    char j;
    long k, l, m;
    char n, o, p;
    } verybig_t;

    #define MY_OFFSET(type, field) ((unsigned long ) &(((type *)
    0)->field))

    int main(int argc, char * argv[])
    {
    printf("MY_OFFSET(verybig_t, a) = 0x%x\n", MY_OFFSET(verybig_t,
    a));
    printf("MY_OFFSET(verybig_t, g) = 0x%x\n", MY_OFFSET(verybig_t,
    g));
    printf("MY_OFFSET(verybig_t, j) = 0x%x\n", MY_OFFSET(verybig_t,
    j));
    printf("MY_OFFSET(verybig_t, p) = 0x%x\n", MY_OFFSET(verybig_t,
    p));
    return(0);
    }
     
    funkyj, Feb 17, 2006
    #1
    1. Advertising

  2. funkyj

    Eric Sosman Guest

    funkyj wrote On 02/17/06 14:44,:
    > PROBLEM STATEMENT:
    > I want to calculate the byte offset of a field with a struct at
    > compile time without instantiating an instance of the struct at
    > runtime AND I want to do this in an ANSI standard compliant
    > fashion.


    Use the offsetof() macro, defined in <stddef.h>.

    > DISCUSSION:
    >
    > Below is a sample program that demonstrates my solution. My questions
    > are:
    >
    > (1) is this ANSI C compliant?


    No.

    > (2) is there a better way to solve this problem?


    Use the offsetof() macro, defined in <stddef.h>.

    > [snipped a variant of the non-portable pre-ANSI substitute
    > for the offsetof() macro, defined in <stddef.h>]


    --
     
    Eric Sosman, Feb 17, 2006
    #2
    1. Advertising

  3. funkyj

    loufoque Guest

    funkyj a écrit :

    > #define MY_OFFSET(type, field) ((unsigned long ) &(((type *)
    > 0)->field))


    You should cast it to uintptr_t rather than unsigned long.
     
    loufoque, Feb 17, 2006
    #3
  4. funkyj

    funkyj Guest

    Thanks! Apparently I wasn't that far off. My local include files
    implement it like this:

    #define __offsetof(type, field) ((size_t)(&((type *)0)->field))
    /* ansi.h */
    #define offsetof(type, member) __offsetof(type, member) /*
    stddef.h */
     
    funkyj, Feb 17, 2006
    #4
  5. On 2006-02-17 14:44:30 -0500, "funkyj" <> said:

    > PROBLEM STATEMENT:
    > I want to calculate the byte offset of a field with a struct at
    > compile time without instantiating an instance of the struct at
    > runtime AND I want to do this in an ANSI standard compliant
    > fashion.
    >
    > DISCUSSION:
    >
    > Below is a sample program that demonstrates my solution. My questions
    > are:
    >
    > (1) is this ANSI C compliant?


    No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)

    > (2) is there a better way to solve this problem?


    Yes. You don't need to solve it, it has already been solved for you.
    Just use the "offsetof" macro already provided for you by standard C.


    --
    Clark S. Cox, III
     
    Clark S. Cox III, Feb 17, 2006
    #5
  6. funkyj

    tedu Guest

    Clark S. Cox III wrote:
    > On 2006-02-17 14:44:30 -0500, "funkyj" <> said:
    > > PROBLEM STATEMENT:
    > > I want to calculate the byte offset of a field with a struct at
    > > compile time without instantiating an instance of the struct at
    > > runtime AND I want to do this in an ANSI standard compliant
    > > fashion.
    > > (1) is this ANSI C compliant?

    >
    > No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)


    assuming one doesn't wish to use offsetof, what potential problems
    exist with the following (aside from non compatible usage)?

    #define almostoffsetof(type, field, o) { \
    type tmp_; \
    o = &tmp_.field - &tmp_; \
    }
     
    tedu, Feb 17, 2006
    #6
  7. funkyj

    Eric Sosman Guest

    tedu wrote On 02/17/06 16:24,:
    > Clark S. Cox III wrote:
    >
    >>On 2006-02-17 14:44:30 -0500, "funkyj" <> said:
    >>
    >>>PROBLEM STATEMENT:
    >>> I want to calculate the byte offset of a field with a struct at
    >>> compile time without instantiating an instance of the struct at
    >>> runtime AND I want to do this in an ANSI standard compliant
    >>> fashion.
    >>> (1) is this ANSI C compliant?

    >>
    >>No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)

    >
    >
    > assuming one doesn't wish to use offsetof,


    Why assume such a silly thing? "Assuming one doesn't
    wish to use parentheses ..."

    > what potential problems
    > exist with the following (aside from non compatible usage)?
    >
    > #define almostoffsetof(type, field, o) { \
    > type tmp_; \
    > o = &tmp_.field - &tmp_; \
    > }


    How about "It doesn't work" -- does that qualify as a
    "potential problem?" Sheesh ...

    --
     
    Eric Sosman, Feb 17, 2006
    #7
  8. funkyj

    Flash Gordon Guest

    funkyj wrote:
    > Thanks! Apparently I wasn't that far off. My local include files
    > implement it like this:
    >
    > #define __offsetof(type, field) ((size_t)(&((type *)0)->field))
    > /* ansi.h */
    > #define offsetof(type, member) __offsetof(type, member) /*
    > stddef.h */


    Just because your system implements offsetof like that does not make it
    portable. In fact, if you use that method, rather than just using the
    offsetof macro provided by your system, it most definitely is *not*
    portable.

    offsetof is provided as a standard macros specifically because it is
    *impossible* to implement it portably.
    --
    Flash Gordon
    Living in interesting times.
    Web site - http://home.flash-gordon.me.uk/
    comp.lang.c posting guidlines and intro -
    http://clc-wiki.net/wiki/Intro_to_clc
     
    Flash Gordon, Feb 17, 2006
    #8
  9. funkyj

    Flash Gordon Guest

    tedu wrote:
    > Clark S. Cox III wrote:
    >> On 2006-02-17 14:44:30 -0500, "funkyj" <> said:
    >>> PROBLEM STATEMENT:
    >>> I want to calculate the byte offset of a field with a struct at
    >>> compile time without instantiating an instance of the struct at
    >>> runtime AND I want to do this in an ANSI standard compliant
    >>> fashion.
    >>> (1) is this ANSI C compliant?

    >> No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)

    >
    > assuming one doesn't wish to use offsetof, what potential problems
    > exist with the following (aside from non compatible usage)?
    >
    > #define almostoffsetof(type, field, o) { \
    > type tmp_; \
    > o = &tmp_.field - &tmp_; \
    > }


    struct tmp_ {int a; int b;} tmp_
    size_t o;

    ....

    if (flag)
    almostoffsetof(tmp_, b, o);
    else
    almostoffsetof(tmp_, a, o);

    Should show you a couple of potential problems, one of which has a
    standard solution the other is rather harder to avoid.
    --
    Flash Gordon
    Living in interesting times.
    Web site - http://home.flash-gordon.me.uk/
    comp.lang.c posting guidlines and intro -
    http://clc-wiki.net/wiki/Intro_to_clc
     
    Flash Gordon, Feb 17, 2006
    #9
  10. "tedu" <> writes:
    [...]
    > assuming one doesn't wish to use offsetof, what potential problems
    > exist with the following (aside from non compatible usage)?
    >
    > #define almostoffsetof(type, field, o) { \
    > type tmp_; \
    > o = &tmp_.field - &tmp_; \
    > }


    It can't be used in an expression.

    --
    Keith Thompson (The_Other_Keith) <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.
     
    Keith Thompson, Feb 17, 2006
    #10
  11. "funkyj" <> writes:
    > PROBLEM STATEMENT:
    > I want to calculate the byte offset of a field with a struct at
    > compile time without instantiating an instance of the struct at
    > runtime AND I want to do this in an ANSI standard compliant
    > fashion.


    Use the offsetof macro defined in <stddef.h>.

    There is no portable way to implement offsetof in standard C. The
    most common implementation dereferences a null pointer (invoking
    undefined behavior) and depends on the compiler to recognize that it
    doesn't actually have to generate code to perform that operation. The
    standard does not guarantee that such an optimization will be
    performed.

    Code in the standard headers are allowed to do whatever non-portable,
    implementation-defined, or just plain ugly things are necessary to
    obtain the required results *with the particular compiler they're
    written to work with*. That's why offsetof is in <stddef.h>. The
    author of <stddef.h> is allowed to invoke undefined behavior as long
    as it happens to yield the required result; you're not.

    --
    Keith Thompson (The_Other_Keith) <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.
     
    Keith Thompson, Feb 17, 2006
    #11
  12. funkyj

    tedu Guest

    Eric Sosman wrote:
    > tedu wrote On 02/17/06 16:24,:
    > > assuming one doesn't wish to use offsetof,

    >
    > Why assume such a silly thing? "Assuming one doesn't
    > wish to use parentheses ..."


    to further one's understanding of C? is that permissible?

    > > what potential problems
    > > exist with the following (aside from non compatible usage)?
    > >
    > > #define almostoffsetof(type, field, o) { \
    > > type tmp_; \
    > > o = &tmp_.field - &tmp_; \
    > > }

    >
    > How about "It doesn't work" -- does that qualify as a
    > "potential problem?" Sheesh ...


    why not? i just noticed it's missing two casts. any other reason?
    #define almostoffsetof(type, field, o) { \
    type tmp_; \
    o = (char *)tmp_.field - (char *)&tmp_; \
    }
    struct hanky { int a; int b; };
    void f(void) {
    struct hanky h;
    int *x;
    char *p;
    ptrdiff_t o;
    almostoffsetof(struct hanky, b, o);
    p = (char *)&h;
    p += o;
    x = (int *)p; /* now x == &h.b */
    }

    why would x not be pointing to h.b?
     
    tedu, Feb 17, 2006
    #12
  13. funkyj

    Jordan Abel Guest

    On 2006-02-17, Keith Thompson <> wrote:
    > "funkyj" <> writes:
    >> PROBLEM STATEMENT:
    >> I want to calculate the byte offset of a field with a struct at
    >> compile time without instantiating an instance of the struct at
    >> runtime AND I want to do this in an ANSI standard compliant
    >> fashion.

    >
    > Use the offsetof macro defined in <stddef.h>.
    >
    > There is no portable way to implement offsetof in standard C. The
    > most common implementation dereferences a null pointer (invoking
    > undefined behavior) and depends on the compiler to recognize that it
    > doesn't actually have to generate code to perform that operation. The
    > standard does not guarantee that such an optimization will be
    > performed.


    I thought that it was guaranteed that the side-effects of the argument
    to sizeof will not be executed. i.e. sizeof printf("Hello, world!\n")
    yields sizeof(int) and does not emit any output.

    What's _not_ guaranteed is the ability to do pointer arithmetic on NULL.

    You could always use a temporary, though, at the expense of not being
    able to use it in an expression. Someone posting elsewhere in this
    thread has done so.

    > Code in the standard headers are allowed to do whatever non-portable,
    > implementation-defined, or just plain ugly things are necessary to
    > obtain the required results *with the particular compiler they're
    > written to work with*. That's why offsetof is in <stddef.h>. The
    > author of <stddef.h> is allowed to invoke undefined behavior as long
    > as it happens to yield the required result; you're not.
    >
     
    Jordan Abel, Feb 17, 2006
    #13
  14. funkyj

    Eric Sosman Guest

    tedu wrote On 02/17/06 17:45,:
    > Eric Sosman wrote:
    >
    >>tedu wrote On 02/17/06 16:24,:
    >>
    >>>assuming one doesn't wish to use offsetof,

    >>
    >> Why assume such a silly thing? "Assuming one doesn't
    >>wish to use parentheses ..."

    >
    >
    > to further one's understanding of C? is that permissible?


    "Question authority." Furthering one's understanding
    is not only permissible, but laudable. Occasionally, one
    can even discover something new and useful by refusing to
    use the established techniques. I read long ago that in
    the fairly early days of electric lighting, new employees
    at the light-bulb factory were instructed to frost the
    insides of the clear bulbs so as to diffuse the light.
    It couldn't be done, of course; this was a "hazing" of the
    new employees, a "sleeveless errand." And one day, a new
    kid who didn't know it was impossible figured out how to
    do it ...

    BUT there's a difference between constructive curiosity
    and silliness that verges on the perverse. It's one thing
    to try to come up with "something better" than offsetof, but
    it's silly to propose something that's obviously worse. If
    you had an idea for a niftier, slicker, and more convenient
    alternative to offsetof, I'd encourage you to post it and
    would be happy to review your effort. But when the thing
    you've come up with is clunkier, stickier, and cruftier than
    what already exists, that's another matter. In fact, it's
    inferior to the already-dismissed-as-non-portable construct
    the O.P. asked about. So why are you fiddling around?

    >>>what potential problems
    >>>exist with the following (aside from non compatible usage)?
    >>>
    >>>#define almostoffsetof(type, field, o) { \
    >>> type tmp_; \
    >>> o = &tmp_.field - &tmp_; \
    >>>}

    >>
    >> How about "It doesn't work" -- does that qualify as a
    >>"potential problem?" Sheesh ...

    >
    >
    > why not? i just noticed it's missing two casts.


    That's a "potential problem," I'd say.

    > any other reason?
    > #define almostoffsetof(type, field, o) { \
    > type tmp_; \
    > o = (char *)tmp_.field - (char *)&tmp_; \
    > }


    What happens when

    #define tmp_ getenv("TMPDIR")

    appears earlier in the file?

    --
     
    Eric Sosman, Feb 17, 2006
    #14
  15. Jordan Abel <> writes:
    > On 2006-02-17, Keith Thompson <> wrote:

    [...]
    >> Use the offsetof macro defined in <stddef.h>.
    >>
    >> There is no portable way to implement offsetof in standard C. The
    >> most common implementation dereferences a null pointer (invoking
    >> undefined behavior) and depends on the compiler to recognize that it
    >> doesn't actually have to generate code to perform that operation. The
    >> standard does not guarantee that such an optimization will be
    >> performed.

    >
    > I thought that it was guaranteed that the side-effects of the argument
    > to sizeof will not be executed. i.e. sizeof printf("Hello, world!\n")
    > yields sizeof(int) and does not emit any output.


    Right (unless the argument is a VLA), but most implementations of
    offsetof() don't use sizeof.

    > What's _not_ guaranteed is the ability to do pointer arithmetic on NULL.


    Right.

    > You could always use a temporary, though, at the expense of not being
    > able to use it in an expression. Someone posting elsewhere in this
    > thread has done so.


    Yes, but that's not an implementation of offsetof.

    --
    Keith Thompson (The_Other_Keith) <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.
     
    Keith Thompson, Feb 17, 2006
    #15
  16. funkyj

    Jordan Abel Guest

    On 2006-02-17, Keith Thompson <> wrote:
    > Jordan Abel <> writes:
    >> On 2006-02-17, Keith Thompson <> wrote:

    > [...]
    >>> Use the offsetof macro defined in <stddef.h>.
    >>>
    >>> There is no portable way to implement offsetof in standard C. The
    >>> most common implementation dereferences a null pointer (invoking
    >>> undefined behavior) and depends on the compiler to recognize that it
    >>> doesn't actually have to generate code to perform that operation. The
    >>> standard does not guarantee that such an optimization will be
    >>> performed.

    >>
    >> I thought that it was guaranteed that the side-effects of the argument
    >> to sizeof will not be executed. i.e. sizeof printf("Hello, world!\n")
    >> yields sizeof(int) and does not emit any output.

    >
    > Right (unless the argument is a VLA), but most implementations of
    > offsetof() don't use sizeof.
    >
    >> What's _not_ guaranteed is the ability to do pointer arithmetic on NULL.

    >
    > Right.


    And that's more likely to be a serious problem than the "phantom
    dereference". [taking the address of a member is essentially the same as
    pointer arithmetic, and has the same problems if it fails]

    >> You could always use a temporary, though, at the expense of not being
    >> able to use it in an expression. Someone posting elsewhere in this
    >> thread has done so.

    >
    > Yes, but that's not an implementation of offsetof.


    No, but it answers the original question well enough.
     
    Jordan Abel, Feb 18, 2006
    #16
  17. funkyj

    tedu Guest

    Eric Sosman wrote:
    > would be happy to review your effort. But when the thing
    > you've come up with is clunkier, stickier, and cruftier than
    > what already exists, that's another matter. In fact, it's
    > inferior to the already-dismissed-as-non-portable construct
    > the O.P. asked about. So why are you fiddling around?


    just for the exercise.

    > > #define almostoffsetof(type, field, o) { \
    > > type tmp_; \
    > > o = (char *)tmp_.field - (char *)&tmp_; \
    > > }

    >
    > What happens when
    >
    > #define tmp_ getenv("TMPDIR")
    >
    > appears earlier in the file?


    you learn not to do that. :)
     
    tedu, Feb 18, 2006
    #17
  18. funkyj

    Guest

    Eric Sosman wrote:
    > tedu wrote On 02/17/06 17:45,:
    > > any other reason?
    > > #define almostoffsetof(type, field, o) { \
    > > type tmp_; \
    > > o = (char *)tmp_.field - (char *)&tmp_; \
    > > }

    >
    > What happens when
    >
    > #define tmp_ getenv("TMPDIR")
    >
    > appears earlier in the file?


    You wish C had namespaces ...

    --
    Paul Hsieh
    http://www.pobox.com/~qed/
    http://bstring.sf.net/
     
    , Feb 18, 2006
    #18
  19. funkyj

    tmp123 Guest

    Keith Thompson wrote:
    > "funkyj" <> writes:
    > > PROBLEM STATEMENT:
    > > I want to calculate the byte offset of a field with a struct at
    > > compile time without instantiating an instance of the struct at
    > > runtime AND I want to do this in an ANSI standard compliant
    > > fashion.

    >
    > There is no portable way to implement offsetof in standard C.


    I suposse the following two methods are not standard due to the pointer
    conversions. But I do not in how many points they break the rules:

    (Another subject, less important in some context, is the limit of
    offset value)
    (Sorry for the amount of parenthesis)

    #include <stdio.h>
    #include <stdlib.h>

    typedef struct
    {
    int f1;
    char f2;
    } s1;

    static void *foo;
    #define offsetof1(x,a) ( (char *)&( ((x *)foo)->a ) - (char *)foo )
    #define offsetof2(x,a) ( *(char *) ( &(((x *)foo)->a) ) )

    int main ( void )
    {
    /* init */
    int i;
    foo=malloc(256);
    for(i=0;i<256;i++) ((char *)foo)=i;

    /* method 1 */
    printf("%d\n",offsetof1(s1,f2));

    /* method 2 */
    printf("%d\n",offsetof2(s1,f2));
    }

    Kind regards.
     
    tmp123, Feb 18, 2006
    #19
  20. "tmp123" <> writes:
    > Keith Thompson wrote:
    >> "funkyj" <> writes:
    >> > PROBLEM STATEMENT:
    >> > I want to calculate the byte offset of a field with a struct at
    >> > compile time without instantiating an instance of the struct at
    >> > runtime AND I want to do this in an ANSI standard compliant
    >> > fashion.

    >>
    >> There is no portable way to implement offsetof in standard C.

    >
    > I suposse the following two methods are not standard due to the pointer
    > conversions. But I do not in how many points they break the rules:
    >
    > (Another subject, less important in some context, is the limit of
    > offset value)
    > (Sorry for the amount of parenthesis)
    >
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > typedef struct
    > {
    > int f1;
    > char f2;
    > } s1;
    >
    > static void *foo;
    > #define offsetof1(x,a) ( (char *)&( ((x *)foo)->a ) - (char *)foo )
    > #define offsetof2(x,a) ( *(char *) ( &(((x *)foo)->a) ) )


    foo is a static pointer object; it's therefore initialized to NULL.
    (Since it's not const, you could mess it up by assigning a value to
    foo.)

    Both are similar to the common definition of offset(), except that
    they use foo rather than a literal 0. Both dereference a null pointer
    and count on the compiler to optimize well enough so that the
    dereference isn't actually executed. offsetof2 seems to assumes that
    a null pointer is all-bits-zero (though it's hard to say, since that
    assumption isn't sufficient to make it work).

    --
    Keith Thompson (The_Other_Keith) <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.
     
    Keith Thompson, Feb 18, 2006
    #20
    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. Lance Riedel

    Translated Offset to Source Offset

    Lance Riedel, Oct 14, 2003, in forum: XML
    Replies:
    2
    Views:
    503
    Patrick TJ McPhee
    Oct 15, 2003
  2. Bradford Chamberlain

    QUERY: field offset rules within structures

    Bradford Chamberlain, Jul 9, 2003, in forum: C Programming
    Replies:
    11
    Views:
    602
    Barry Schwarz
    Jul 13, 2003
  3. Chris Fogelklou
    Replies:
    36
    Views:
    1,391
    Chris Fogelklou
    Apr 20, 2004
  4. Sound
    Replies:
    2
    Views:
    454
    Randy Webb
    Sep 28, 2006
  5. Roy Smith
    Replies:
    4
    Views:
    277
    Roy Smith
    Jan 27, 2013
Loading...

Share This Page