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

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

1. ### funkyjGuest

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

2. ### Eric SosmanGuest

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

3. ### loufoqueGuest

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
4. ### funkyjGuest

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
5. ### Clark S. Cox IIIGuest

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
6. ### teduGuest

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
7. ### Eric SosmanGuest

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
8. ### Flash GordonGuest

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
9. ### Flash GordonGuest

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
10. ### Keith ThompsonGuest

"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
11. ### Keith ThompsonGuest

"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
12. ### teduGuest

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
13. ### Jordan AbelGuest

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

> 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
14. ### Eric SosmanGuest

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

>>>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
15. ### Keith ThompsonGuest

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

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
16. ### Jordan AbelGuest

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

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

No, but it answers the original question well enough.

Jordan Abel, Feb 18, 2006
17. ### teduGuest

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

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
18. ### 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
19. ### tmp123Guest

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
20. ### Keith ThompsonGuest

"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